Metadata-Version: 2.4
Name: blurred-concepts-janus
Version: 2.1.0
Summary: Persona-Driven Decision Engine for Blurred Concepts
Author: Blurred Concepts
License-Expression: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: <3.13,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: structlog>=24.0.0
Requires-Dist: jsonschema>=4.23.0
Requires-Dist: referencing>=0.28.4
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: setuptools>=61.0; extra == "dev"
Requires-Dist: wheel; extra == "dev"
Requires-Dist: pytest>=9.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
Requires-Dist: ruff>=0.15.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: pre-commit>=3.6.0; extra == "dev"
Dynamic: license-file

# Janus

> Policy-bound decision authority for Blurred Concepts.

Janus evaluates one complete request and emits exactly one directive. It does
not create execution tickets, grant authority, load Shards, or execute work.
See [JANUS_DOCTRINE.md](JANUS_DOCTRINE.md) for the boundary and
[the canonical v2 protocol](contracts/v2/protocol.md) for wire semantics.

## Identity

- Distribution: `blurred-concepts-janus`
- Import namespace: `janus`
- CLI: `janus`
- Current release: `2.1.0`
- Wire schema version: `2.0`
- Supported Python: 3.10–3.12

## Installation

```bash
python -m pip install blurred-concepts-janus
```

For development:

```bash
python -m pip install -e ".[dev]"
```

## Decision API

```python
import asyncio
from datetime import datetime, timezone

from janus import (
    DecisionEngineV2,
    DecisionEventV2,
    DecisionRequestV2,
    DecisionStateV2,
    PersonaIdentityV2,
)

request = DecisionRequestV2(
    request_id="example-request",
    timestamp=datetime.now(timezone.utc),
    persona=PersonaIdentityV2(
        type="audience",
        id="contact-intake",
        policy_id="audience.crm-contact-upsert",
        policy_version=1,
        context={"tenant_id": "example-tenant"},
    ),
    event=DecisionEventV2(
        type="lead.received",
        payload={"email": "person@example.com"},
    ),
    state=DecisionStateV2(),
)

directive = asyncio.run(DecisionEngineV2().decide(request))
print(directive.model_dump_json())
```

`decide` remains the directive-only compatibility surface. Consumers that
explicitly need bounded model diagnostics or continuation correlation use
`DecisionEngineV2.evaluate` and receive a `DecisionEvaluationV2` envelope:

```python
evaluation = asyncio.run(DecisionEngineV2().evaluate(request))
print(evaluation.model_dump_json())
```

The checked-in persona policy owns event-to-action-policy routes. A resolved
action policy fixes the legal shard, action, and business-input allowlist. A
model is advisory only and cannot select those fields. Fabric separately
resolves execution constraints and trusted caller authority. The complete,
normative route/model outcome matrix is in the
[v2 protocol](contracts/v2/protocol.md#total-evaluation-outcome-matrix).

## Actionless Advisor

The canonical `advisor.business-strategy` persona has no action route and
cannot emit `ACTIVATE_SHARD`. It can return only bounded `ADVISE` or
`REQUEST_INPUT` results according to its policy and the total outcome matrix.

Advisor evaluation requires an external `ModelPort`; Janus does not ship a
provider adapter. Knowledge retrieval happens outside Janus. Composition may
place bounded recall into `DecisionStateV2.ogham_recall`, and Janus projects
only policy-allowed decision inputs and recall summaries into the port. Model
content cannot become an action, ticket, scope, grant, timeout, or credential.

## Continuations

An opt-in evaluation may return a caller-held `DecisionContinuationV2` when a
policy-valid routed or actionless decision needs one or more allowed fields.
Supply it beside a complete request in `DecisionInvocationV2`; Janus revalidates
all correlation, persona, policy-version, event, route state, and
requested-field constraints before calling a model.

Continuations are stateless from Janus's perspective: the package stores no
session. Continuations grant no authority, do not bypass full evaluation, and
do not provide replay or consumption state, expiry, or single-use enforcement.
Callers own any replay or lifecycle state, and Fabric validates trusted
authority again for every activation.

## Runtime ownership

Janus owns the canonical v2 decision schemas and policy records. The complete
runtime is composed outside this package:

1. composition may recall bounded operational context from Ogham;
2. Janus evaluates the request and emits one directive;
3. Fabric validates trusted caller context, derives a least-authority ticket,
   enforces the execution timeout, and preserves the terminal outcome;
4. the selected Shard executes only the authorized activation and cannot
   broaden its ticket; and
5. composition attempts to record a redacted terminal memory in Ogham.

Janus does not own Fabric execution, retries, or queues. Providers, Shards, and
AetherSDK remain external. Ogham owns operational memory. MIMIR² owns
development-time comprehension. No LBP runtime runs inside Janus.

Ordinary, recoverable Ogham service failures raised as `Exception` become
warnings. They do not block a Janus decision, turn a Fabric success into a
failure, or hide a Fabric failure. `CancelledError`, `SystemExit`,
`KeyboardInterrupt`, and other process-control `BaseException` signals propagate
and must not be swallowed.
MIMIR² is development-time comprehension infrastructure and is never a runtime
memory dependency.

Provider ownership stays below the Shard boundary. For the first CRM slice,
AetherSDK owns credentials, provider normalization, the stable HubSpot contact
identifier, and the structured provider operation result. Its legacy
`push(...) -> bool` surface is compatibility-only; new integration code consumes
the structured result.

The neutral CRM contact port between Shard logic and AetherSDK isolates Shard
policy and execution from provider result-shape evolution. A structured-result
change terminates in the adapter/port translation: that translation may need to
change, but Shard policy and execution semantics do not.

Primoria remains an optional implementation supplied through Janus's bounded
`ModelPort`. This repository does not ship or select a production Primoria
adapter, and model advice cannot grant authority or choose an unallowlisted
action.

## Installed-artifact proof

Release verification includes a Janus-local installed-wheel proof. It builds
the wheel and sdist without dependency resolution, installs the wheel into a
fresh environment outside the checkout, and exercises the public runtime,
Advisor, diagnostics, continuation, CLI, and offline policy-tool surfaces. The
probe audits distribution ownership and declared dependencies and rejects
checkout imports, undeclared packages, runtime Ogham/MIMIR²/LBP/provider
dependencies, and accidental test or script imports.

```bash
python -m build --no-isolation
python -m pytest tests/test_installed_general_surface.py tests/test_contract_artifacts_v2.py
```

The fleet-level vertical-slice runner remains Shards-owned. It accepts explicit
Janus, AetherSDK, and Shards repository paths, creates sanitized temporary
source copies, builds wheels, and tests those installed wheels in an isolated
offline/no-index environment. The test runtime denies DNS, TCP, and UDP access,
including from child Python processes. This keeps the proof independent of
checkout imports, local build residue, and live provider services.

From a Shards checkout with sibling repositories, run:

```bash
python scripts/run_artifact_e2e.py --janus-repo ../Janus --aethersdk-repo ../AetherSDK --shards-repo .
```

The three explicit repository paths may point anywhere. Build and test
dependencies must already be provisioned at their pinned versions; the runner
does not resolve them from the network.

The proof substitutes only named external seams: a bounded local Ogham fake,
deterministic clock and ID-factory seams, deterministic model candidates, and
HubSpot HTTP interception through `respx`. The shipped Janus, Fabric, Shards,
and AetherSDK production composition otherwise runs as real installed-wheel
code, with no live network.

## CLI

Canonical decision documents use explicit file boundaries:

```bash
janus decide --request-file decision-request.json
janus evaluate --request-file decision-invocation.json
```

`decide` accepts a `DecisionRequestV2` and emits exactly one `DirectiveV2`.
`evaluate` is opt-in: it accepts a `DecisionInvocationV2` and emits one
`DecisionEvaluationV2` containing diagnostics and any continuation. Both write
one canonical UTF-8 JSON document to stdout; controlled diagnostics go to
stderr.

Policy review commands are deterministic and offline. They read only explicit
local directories, and `scaffold` writes a template to stdout without creating
files:

```bash
janus policy validate contracts/v2/policies
janus policy diff policy-before policy-after
janus policy scaffold persona
janus policy scaffold action
```

The supported commands are:

| Command | Purpose |
|---|---|
| `janus decide --request-file PATH` | Emit the directive for one canonical request |
| `janus evaluate --request-file PATH` | Opt into evaluation diagnostics and continuation correlation |
| `janus policy validate DIRECTORY` | Validate canonical persona/action policy files offline |
| `janus policy diff BEFORE AFTER` | Compare two canonical policy directories offline |
| `janus policy scaffold KIND` | Print a canonical `persona` or `action` scaffold offline |
| `janus version` | Print the installed package version |

`janus decide <email> --json` is the one-window CRM convenience compatibility
surface, not the canonical document interface. The one-window `assistant` and
`agent` persona aliases normalize to `advisor` and `shard`; either compatibility
path records consumer ID/version evidence without input payloads.

## Canonical references

- [v2 protocol](contracts/v2/protocol.md)
- [v2 generated schemas](contracts/v2/schemas/)
- [v2 policy records](contracts/v2/policies/)
- [Janus doctrine](JANUS_DOCTRINE.md)
- [security guidance](SECURITY.md)
- [development workflow](CONTRIBUTING.md)

The schemas and policies are canonical artifacts; this README does not restate
their fields or invariants.

## Legacy migration

Frozen v1 routing and executable Shard interfaces exist only under the explicit
`janus.legacy` namespace and are unreachable from the default package and CLI
surfaces. Compatibility telemetry records only consumer identity and version;
it must never capture request payloads, provider data, credentials, directives,
tickets, or memory content. See
[the legacy migration note](janus/legacy/README.md) for the removal gates. New
consumers must use v2.

## Git workflow

Normal feature work branches from and targets `develop`. Only release or
stabilization promotion and emergency `hotfix/*` work targets `main`.

## License

MIT License — see [LICENSE](LICENSE).
