Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 2 deletions .github/workflows/test-vectors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
run: pip install cryptography argon2-cffi
- name: Run all suites (v2.5 + v3.0 + adversarial + v4.0-preview)
run: python3 verify_vectors.py
- name: Run klickd Python package tests (incl. v4 preview round-trip)
- name: Run klickd Python package tests (incl. v4 preview round-trip + v4 GA strict validation)
working-directory: packages/pypi/klickd
run: |
pip install -e .
pip install -e ".[validate]"
pip install pytest
python -m pytest tests/ -q

Expand Down
54 changes: 45 additions & 9 deletions packages/pypi/klickd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,53 @@ payload = load_klickd(file_bytes, passphrase="my-passphrase", legacy=True)

---

## `.klickd` v4 preview fields (additive, non-GA)
## `.klickd` v4 — GA strict + preview fields

This library currently targets the **v3** envelope and is **stable at v3.5.1**.
The v4 preview track (`v4.0.0-preview.1`, NOT GA) introduces additive payload
fields such as `profile_kind`, `media_profile`, `verification_gates`,
`claim_sources`, `verification_artifacts`, `migration`, and `context_cost`.
This library targets the **v3** envelope on the wire (envelope crypto contract
is frozen at v3 per SPEC.md §33.10 #2) and ships the **v4 GA strict candidate
payload schema** for validation. The v4 preview track (`v4.0.0-preview.1`)
remains accepted in parallel.

These fields are **preserved verbatim** on round-trip — `load_klickd` returns
the raw decrypted JSON object and `save_klickd` re-encrypts it without
filtering unknown keys. Strict v4 validation, migrations, and business-logic
helpers are intentionally **not** implemented yet.
The v4 additive payload fields — `profile_kind`, `media_profile`,
`verification_gates`, `human_veto_policy`, `claim_sources`,
`verification_artifacts`, `migration`, `context_cost`, `deprecated_fields` —
are **preserved verbatim** on round-trip (SPEC.md §33.7). `load_klickd`
returns the raw decrypted JSON object; `save_klickd` re-encrypts it without
filtering unknown keys.

### Optional v4 schema validation

Install the optional `validate` extra to enable strict / preview schema
validation against the bundled v4 JSON schemas (RFC-001 v1, RFC-002 v1
core, RFC-004 v1):

```bash
pip install klickd[validate]
```

```python
from klickd import validate, validate_iter_errors

payload = {
"payload_schema_version": "4.0",
"verification_gates": {
"version": 1,
"gates": [
{"action_class": "public_post", "level": "block"},
],
},
}

validate(payload, strict=True) # raises KlickdError(KLICKD_E_SCHEMA) on fail

# Non-raising variant that returns every (path, message):
errors = validate_iter_errors(payload, strict=True)
```

`validate(..., strict=False)` selects the permissive v4 preview schema.
`validate(..., target="unified")` selects the unified envelope+payload
schema. The strict schema accepts both `"4.0"` and `"4.0.0-preview.1"`
in `payload_schema_version` so preview files round-trip without rewriting.

```python
v4_preview_payload = {
Expand Down
8 changes: 8 additions & 0 deletions packages/pypi/klickd/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@ dependencies = [
"typing-extensions>=4.8",
]

[project.optional-dependencies]
# Optional extra for v4 JSON-Schema validation. The base SDK can load/save
# .klickd files without jsonschema; only klickd.validate.validate() needs it.
validate = ["jsonschema>=4.18"]

[project.urls]
Homepage = "https://klickd.app"
Repository = "https://github.com/Davincc77/klickdskill"
Documentation = "https://github.com/Davincc77/klickdskill/blob/main/SPEC.md"

[tool.hatch.build.targets.wheel]
packages = ["src/klickd"]
# Schema files under src/klickd/schemas/ are part of the package tree and
# auto-included by hatchling when packages = ["src/klickd"] selects the
# parent directory. No explicit include rule is required.
17 changes: 17 additions & 0 deletions packages/pypi/klickd/src/klickd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,29 @@
from .decode import load_klickd
from .encode import save_klickd
from .errors import KlickdError, KlickdErrorCode, HTTP_STATUS
from .validate import validate, validate_iter_errors
from ._types import (
KlickdPayload,
KlickdEnvelope,
KlickdMemoryEntry,
KlickdIdentity,
KlickdContext,
KlickdKnowledge,
KlickdMediaProfileEntry,
KlickdMediaProfileV1,
KlickdGateEntry,
KlickdVerificationGatesV1,
KlickdHumanVetoPolicy,
KlickdClaimSources,
KlickdMigrationV1,
)

__version__ = "4.0.0a1"
__all__ = [
"load_klickd",
"save_klickd",
"validate",
"validate_iter_errors",
"KlickdError",
"KlickdErrorCode",
"HTTP_STATUS",
Expand All @@ -31,5 +41,12 @@
"KlickdIdentity",
"KlickdContext",
"KlickdKnowledge",
"KlickdMediaProfileEntry",
"KlickdMediaProfileV1",
"KlickdGateEntry",
"KlickdVerificationGatesV1",
"KlickdHumanVetoPolicy",
"KlickdClaimSources",
"KlickdMigrationV1",
"__version__",
]
81 changes: 81 additions & 0 deletions packages/pypi/klickd/src/klickd/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,67 @@ class KlickdKnowledge(TypedDict, total=False):
next_steps: List[str]


class KlickdMediaProfileEntryHash(TypedDict):
algo: Literal["blake3"]
value: str


class KlickdMediaProfileEntry(TypedDict, total=False):
id: Required[str]
modality: Required[Literal["voice", "image", "document", "embedding"]]
hash: Required[KlickdMediaProfileEntryHash]
label: str
language: str
uri: str
media_type: str
byte_size: int
duration_ms: int
bytes_b64: str
producer: dict
consent: dict


class KlickdMediaProfileV1(TypedDict, total=False):
version: Required[Literal[1]]
entries: Required[List[KlickdMediaProfileEntry]]


KlickdGateLevel = Literal["silent", "warn", "confirm", "block", "require-owner"]


class KlickdGateEntry(TypedDict, total=False):
action_class: Required[str]
level: Required[KlickdGateLevel]
id: str
reason: str


class KlickdVerificationGatesV1(TypedDict, total=False):
version: Required[Literal[1]]
gates: Required[List[KlickdGateEntry]]
user_default: KlickdGateLevel


class KlickdHumanVetoPolicy(TypedDict, total=False):
applies_to: List[str]
second_party: Optional[str]
min_level: KlickdGateLevel
rationale: str


class KlickdClaimSources(TypedDict, total=False):
prefer: List[str]
require_citation_for: List[str]
records: List[Any]


class KlickdMigrationV1(TypedDict, total=False):
source_version: str
migrated_at: str
migration_report_ref: str
backup_ref: str


class KlickdPayload(TypedDict, total=False):
payload_schema_version: Required[str]
domain_schema_version: Required[str]
Expand All @@ -84,3 +145,23 @@ class KlickdPayload(TypedDict, total=False):
context: KlickdContext
knowledge: KlickdKnowledge
memory: List[KlickdMemoryEntry]
# v4 additive surface (preview + GA). Strict shape on v1-frozen fields.
profile_kind: str
preview: str
onboarding_trigger: str
media_profile: Union[KlickdMediaProfileV1, dict]
verification_gates: Union[KlickdVerificationGatesV1, dict]
human_veto_policy: Optional[KlickdHumanVetoPolicy]
claim_sources: Optional[KlickdClaimSources]
migration: Optional[KlickdMigrationV1]
risk_thresholds: Optional[dict]
preflight_checks: Optional[List[Any]]
error_journal: Optional[List[Any]]
verification_artifacts: Optional[List[Any]]
contract_tests: Optional[List[Any]]
success_criteria: Optional[Any]
reversibility: Optional[dict]
blast_radius: Optional[dict]
context_cost: Optional[dict]
gaming_profile: Optional[dict]
deprecated_fields: Optional[List[Any]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://klickd.app/schemas/v4-preview/klickd-payload.schema.json",
"title": "klickd Payload v4 Preview (PERMISSIVE, NON-NORMATIVE)",
"description": "PREVIEW schema for the .klickd v4 inner payload, targeting v4.0.0-preview.1. This schema is INTENTIONALLY PERMISSIVE: it accepts and preserves draft v4 structures (media_profile, verification_gates, human_veto_policy, claim_sources, verification_artifacts, migration, context_cost, profile_kind) without performing strict validation. additionalProperties is true at every level so that unknown fields round-trip verbatim. This schema MUST NOT be used to reject a file that is otherwise a valid v3.5.1 file. The normative schemas remain klickd-envelope-v3.schema.json and klickd-payload-v3.schema.json. See SPEC.md §33 and docs/rfcs/ for the design source.",
"type": "object",
"additionalProperties": true,
"properties": {
"preview": {
"type": "string",
"description": "Marks a file as belonging to a specific preview iteration. Recommended value for files written against this schema: 'v4.0.0-preview.1'. Absent in v3.x files."
},
"payload_schema_version": {
"type": "string",
"description": "Payload schema version. PERMISSIVE in this preview: any string is accepted. A future strict v4 schema MAY pin this to a concrete value."
},
"domain_schema_version": {
"type": "string",
"description": "Domain-specific schema version (e.g. 'education-1.2'). PERMISSIVE in this preview."
},
"profile_kind": {
"type": "string",
"description": "Top-level discriminator for the profile shape. Common values: 'learner', 'agent', 'team', 'robot'. v3.x is implicitly 'learner'; the preview makes this explicit and extensible. Custom strings are permitted."
},
"media_profile": {
"description": "RFC-001: portable, hash-referenced media context. Bytes live outside the .klickd file by default; the payload carries metadata, hashes, and (optionally) inline base64 below a documented threshold. PERMISSIVE: structure is not enforced here.",
"type": ["object", "array", "null"],
"additionalProperties": true
},
"verification_gates": {
"description": "RFC-002 v1: user's preferred friction profile, mapping action class to gate level (silent / warn / confirm / block / require-owner). PERMISSIVE: not enforced here.",
"type": ["object", "array", "null"],
"additionalProperties": true
},
"human_veto_policy": {
"description": "RFC-002 v1: standing rules about when a human MUST be in the loop, regardless of agent confidence. Overrides any lower gate. PERMISSIVE: not enforced here.",
"type": ["object", "null"],
"additionalProperties": true
},
"claim_sources": {
"description": "RFC-002 v1 + v2-additive: declarative preferences for where to ground factual claims, and records of what was actually used. PERMISSIVE: not enforced here.",
"type": ["object", "null"],
"additionalProperties": true
},
"verification_artifacts": {
"description": "RFC-002 §8b.8: pointer ledger of outputs already produced by expensive verification commands (test suites, builds, web fetches, DOI resolutions). MUST be a pointer ledger, not a payload sink. PERMISSIVE: structure is not enforced here.",
"type": ["array", "null"]
},
"error_journal": {
"description": "RFC-002 v1: append-only lessons learned that should influence future gate evaluation. PERMISSIVE: not enforced here.",
"type": ["array", "null"]
},
"risk_thresholds": {
"description": "RFC-002 v1: numeric / categorical knobs (e.g. public_reach, financial_amount_eur_max_silent). PERMISSIVE.",
"type": ["object", "null"],
"additionalProperties": true
},
"preflight_checks": {
"description": "RFC-002 v1: small, named checks an agent SHOULD run before acting on certain classes. PERMISSIVE.",
"type": ["array", "null"]
},
"contract_tests": {
"description": "RFC-002 v2-additive: machine-checkable contract tests bound to action classes. PERMISSIVE.",
"type": ["array", "null"]
},
"success_criteria": {
"description": "RFC-002 v2-additive: declarative success criteria per action class. PERMISSIVE.",
"type": ["object", "array", "null"],
"additionalProperties": true
},
"reversibility": {
"description": "RFC-002 v2-additive: declared reversibility for action classes. PERMISSIVE.",
"type": ["object", "null"],
"additionalProperties": true
},
"blast_radius": {
"description": "RFC-002 v2-additive: declared blast radius for action classes. PERMISSIVE.",
"type": ["object", "null"],
"additionalProperties": true
},
"migration": {
"description": "RFC-004: optional migration metadata block. Audit-only in this preview — no migration tooling ships with v4.0.0-preview.1. PERMISSIVE.",
"type": ["object", "null"],
"additionalProperties": true,
"properties": {
"source_version": {"type": "string"},
"migrated_at": {"type": "string"},
"migration_report_ref": {"type": "string"},
"backup_ref": {"type": "string"}
}
},
"context_cost": {
"description": "Research/benchmark track (benchmarks/context_cost/RFC.md): optional fields recording measured 'repeated context waste' for this profile. No normative semantics depend on this field. PERMISSIVE.",
"type": ["object", "null"],
"additionalProperties": true
}
}
}
Loading
Loading