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
17 changes: 16 additions & 1 deletion .github/workflows/python-package-poetry.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
name: Python Package using Poetry

on: [push]
on:
push:
pull_request:

jobs:
version-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python 3.12.8
uses: actions/setup-python@v3
with:
python-version: '3.12.8'

- name: Check pyproject.toml / setup.py / CHANGELOG.md versions agree
run: python3 scripts/check_version.py

build-linux:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
runs-on: ubuntu-latest
strategy:
max-parallel: 5
Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow publishes to PyPI when a `v*` tag is pushed. The tag push
# IS the release: nothing else in this repo should run `poetry publish`,
# `twine upload`, or cut a GitHub release by hand. See CHANGELOG.md 0.10.0
# for the rationale (this project was previously triggering on
# `release: published`, which decoupled the release event from the tag and
# commit it was supposed to represent).
#
# Auth: this workflow uses a stored PyPI API token (secrets.PYPI_API_TOKEN),
# not PyPI trusted publishing (OIDC). Switching to trusted publishing would
# need the project registered for it on PyPI first, so that migration is
# left as a separate follow-up; this change only fixes the trigger and adds
# the pre-publish version guard.
#
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
Expand All @@ -9,8 +19,9 @@
name: Upload Python Package

on:
release:
types: [published]
push:
tags:
- 'v*'

permissions:
contents: read
Expand All @@ -26,6 +37,8 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: '3.x'
- name: Check that the tag matches pyproject.toml's version
run: python3 scripts/check_version.py --tag "${GITHUB_REF_NAME}"
- name: Install Poetry
run: |
curl -sSL https://install.python-poetry.org | python -
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,8 @@ cython_debug/
.idea/

# MAC OS
.DS_Store
.DS_Store

# oh-my-claudecode plugin scratch (session ids, tool throttles, per-machine
# absolute worktree paths) -- never commit this
.omc/
7 changes: 7 additions & 0 deletions API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ sentiment = vcon.find_analysis_by_type("sentiment")

##### Tag Management

Tags are stored as a `"tags"`-purpose attachment whose body is a list of
`"name:value"` strings (per `draft-ietf-vcon-vcon-core-04`, a `"json"`-encoded
body is the JSON value itself, not a serialized string). `get_tag()` and
`add_tag()` also accept a legacy JSON-*string* tags body -- as written under
-02 conventions, or by this library prior to 0.10.0 -- decoding it
automatically; `add_tag()` normalizes it back to a list in place.

###### `add_tag(tag_name: str, tag_value: str) -> None`
Add a tag to the vCon.

Expand Down
79 changes: 79 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,84 @@
# Changelog

## [0.10.0] - Unreleased

Retargets the library to `draft-ietf-vcon-vcon-core-04`. Under -04 section
2.3.2, a `"json"`-encoded `body` is the JSON *value* itself (object, array,
string, number, bool, or null), not a `json.dumps` string. `add_tag()`'s
list body and `add_lawful_basis_attachment()`'s object body were already
correct under -04; this release brings readers, defaults, and the WTF/
lawful-basis extensions in line with the same convention, and fixes a
handful of adjacent spec-compliance gaps.

### Changed
- **Breaking (output shape):** every attachment created via `add_attachment()`,
`add_lawful_basis_attachment()`, and `add_wtf_transcription_attachment()`
now defaults `party` and `dialog` to `0` when not supplied (matching
`add_tag()`'s existing convention), `start` to the vCon's `created_at`,
and `mediatype` to `"application/json"` for `encoding="json"` bodies.
The WG JSON schema requires `start`, `party`, `dialog` on every
attachment; previously these were omitted unless explicitly passed,
producing non-compliant output. Callers that inspected the *absence* of
these keys (rather than their values) will see a difference.
- `Attachment.__init__` applies the same `party`/`dialog`/`mediatype`
defaults directly, so `Attachment(...)` constructed outside of a `Vcon`
gets the same defaults as `Vcon.add_attachment()`.
- `Dialog.to_dict()` no longer emits empty `meta: {}` / `metadata: {}`
placeholders. Both attributes are still initialized internally for
backward-compatible attribute access (`dialog.meta`, `dialog.metadata`);
only the serialized form changes.
- Inline `base64url` bodies written by the library (`Dialog.add_image_data()`,
`Dialog.add_video_data()`, `Dialog.transcode_video()`, `Dialog.to_inline_data()`,
`Attachment.from_image()`) are now unpadded, per -04's JWS/RFC 7515-style
base64url convention. New `vcon.dialog.b64url_encode()` /
`vcon.dialog.b64url_decode()` helpers encode without padding and decode
padded-or-unpadded input respectively; internal decode call sites
(`add_video_data`, `generate_thumbnail`, `extract_video_metadata`,
`transcode_video`) were updated to use `b64url_decode()` so both old
(padded) and new (unpadded) bodies still decode.
- `.github/workflows/python-publish.yml` now triggers on `push: tags:
['v*']` instead of `release: published`. The tag push is the release;
nothing else should publish. Publishing auth is unchanged (a stored
`PYPI_API_TOKEN` secret via `pypa/gh-action-pypi-publish`) -- this repo's
existing workflow was not already using PyPI trusted publishing (OIDC),
so switching auth methods was out of scope for this change and is left
as a separate follow-up.

### Added
- `vcon.body.decode_body(entry)` (also exposed as `Vcon.decoded_body(entry)`),
a shared helper that decodes an attachment/analysis/dialog `body`,
accepting both the -04 shape (body already the decoded value) and a
legacy JSON-encoded string body (as written under -02 conventions, or by
this library prior to 0.10.0). Used by `Vcon.get_tag()`/`add_tag()` and
by the lawful-basis and WTF extension validators/processors, so every
body-reading call site in the library now accepts both shapes.
- `vcon.dialog.b64url_encode()` / `vcon.dialog.b64url_decode()`, public
unpadded-base64url encode/decode helpers (see Changed, above).
- `tests/schema/vcon_json_schema.json`, a vendored copy of the vCon working
group's reference JSON schema (see `tests/schema/SOURCE.md` for
provenance), plus `tests/test_schema_compliance.py`, which builds a vCon
using only public helpers (`add_tag`, `add_lawful_basis_attachment`,
`add_dialog`, `add_attachment`) and validates it against that schema with
no post-processing. `jsonschema` added as a dev dependency.
- `scripts/check_version.py`, a version-consistency guard: compares
`pyproject.toml`'s version against the newest `## [x.y.z]` CHANGELOG
heading and (if present) `setup.py`'s hardcoded version, and, given
`--tag <ref>`, also checks a release tag against the pyproject version.
Wired into CI on every push/PR, and into the publish workflow immediately
before publishing so a release cut from the wrong commit fails loudly.

### Fixed
- `Vcon.get_tag()` and `Vcon.add_tag()` now accept a legacy JSON-string
`tags` body (written under -02 conventions, or by this library prior to
0.10.0) as well as the -04 list-valued body. `add_tag()` normalizes a
legacy string body to a list in place, preserving existing tags, before
appending the new one.
- The lawful-basis and WTF extension validators/processors
(`extensions/lawful_basis/validation.py`, `extensions/lawful_basis/processing.py`,
`extensions/wtf/validation.py`, `extensions/wtf/processing.py`,
`extensions/wtf/extension.py`) now decode a legacy JSON-string attachment
body before working with it, instead of assuming it is already a `dict`.

## [0.9.6] - 2026-06-04

### Security
Expand Down
26 changes: 25 additions & 1 deletion MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,28 @@ vcon.save_to_file("conversation.json")
- Check the full documentation in `docs/source/new_required_fields.rst`
- Run the example: `python samples/example_new_fields.py`
- Review the test files for usage examples
- Open an issue if you encounter problems
- Open an issue if you encounter problems

## Migrating to 0.10.0 (draft-ietf-vcon-vcon-core-04)

0.10.0 retargets the library to `draft-ietf-vcon-vcon-core-04`. The
specification's body semantics did not change from prior guidance in this
library (a `"json"`-encoded body was always meant to be a JSON value, not a
string), but two behavior changes follow from aligning with the WG's
reference JSON schema:

- **Legacy string bodies are now read transparently.** If you (or an
adapter) previously wrote a `"json"`-encoded body as a `json.dumps`
string -- for example a `tags` attachment body written as
`'["category:support"]'` instead of `["category:support"]` -- `get_tag()`,
`add_tag()`, and the lawful-basis/WTF extension validators now decode
that string automatically. `add_tag()` also normalizes a legacy string
body back to a list, in place, the first time you call it on that vCon.
You don't need to migrate existing data by hand.
- **New attachments get more defaults filled in.** `add_attachment()`,
`add_lawful_basis_attachment()`, and `add_wtf_transcription_attachment()`
now default `party`/`dialog` to `0` and `start` to the vCon's
`created_at` when you don't supply them, because the WG JSON schema
requires `start`, `party`, and `dialog` on every attachment. If your code
checked for the *absence* of these keys (rather than their values), check
the CHANGELOG's 0.10.0 entry before upgrading.
Loading
Loading