Warnings cleanup

This commit is contained in:
2026-05-14 01:02:25 +02:00
parent 484a978795
commit 41bd662641
12 changed files with 331 additions and 41 deletions

View File

@@ -78,6 +78,8 @@ CMIS integration:
- `getContentStream` returns actual bytes/stream with content headers,
- `setContentStream` stores through deduplicating representation service,
- `appendContentStream` composes the current stream plus appended bytes and
stores the resulting representation through the same deduplicating service,
- content stream changes produce versions and audit events,
- descriptors remain available for clients that only need metadata.

View File

@@ -1,14 +1,13 @@
# CMIS 1.1 Capability Scorecard
Date: 2026-05-13
Date: 2026-05-14
Evidence update: the 2026-05-13 WP-0014 OpenCMIS pass completed the selected
Evidence update: the 2026-05-14 release-warning pass completed the selected
Browser Binding `repository-type` and `object-content` baseline. The latest
run, `run-20260513T210255Z`, reports `0` unexpected findings:
`repository-type` is warning-only because the local harness uses HTTP, and
`object-content` is warning-only because `appendContentStream()` is not
supported. See
`docs/cmis-opencmis-tck-wp0014-evidence-2026-05-13T210255Z.md`.
run, `run-20260513T223537Z`, reports `0` unexpected findings. The
`object-content` group now passes without warnings; the only remaining
OpenCMIS warning is the local harness using HTTP rather than HTTPS. See
`docs/cmis-opencmis-tck-release-readiness-evidence-2026-05-13T223537Z.md`.
The score below remains a product-depth estimate against mature CMIS products.
The selected OpenCMIS baseline is now stable preparation evidence for
@@ -72,10 +71,10 @@ CMIS interoperability importance rather than engine-internal importance.
| Metric | Score |
| --- | ---: |
| OpenCMIS selected-baseline infrastructure score | 98.3% |
| Weighted CMIS 1.1 depth vs Hyland Alfresco benchmark | 59% |
| Controlled-client Browser Binding usefulness | 80% |
| Broad commodity CMIS client compatibility | 54% |
| OpenCMIS selected-baseline infrastructure score | 99.1% |
| Weighted CMIS 1.1 depth vs Hyland Alfresco benchmark | 60% |
| Controlled-client Browser Binding usefulness | 82% |
| Broad commodity CMIS client compatibility | 55% |
Interpretation: the OpenCMIS infrastructure score measures the selected
`repository-type` and `object-content` harness baseline only. The current CMIS
@@ -92,8 +91,8 @@ broad ECM/CMIS replacement surface.
| Type definitions | 6 | 55% | Hyland Alfresco ACS | - Base types, Browser Binding type definitions, secondary type projection, and nullable content stream properties exist.<br>- No mutable types or custom schema/type management.<br>- Property definition depth remains intentionally narrow. |
| Navigation service | 8 | 62% | Hyland Alfresco ACS | - Root and folder-scoped children, path lookup, folder parent lookup, parent path segments, move, and delete-tree work in the selected baseline.<br>- Projection-only parents exist.<br>- Missing `getDescendants`, `getFolderTree`, and real filing mutations. |
| Object read service | 10 | 84% | Hyland Alfresco ACS | - Object envelopes, properties, content descriptors, ACL projection, relationships, allowable actions, property filters, and path-addressed Browser Binding reads exist.<br>- Deleted/hidden objects are correctly not exposed.<br>- OpenCMIS object/content read-side baseline now completes with warnings only. |
| Object write service | 8 | 70% | Hyland Alfresco ACS | - `createDocument`, `createFolder`, scoped `moveObject`, folder rename, selected standard property updates, custom metadata updates, content stream set/delete, `bulkUpdateProperties`, and `createDocumentFromSource` exist.<br>- No broad filing mutation, raw physical delete, checkout/checkin, or policy/item creation.<br>- Delete remains intentionally governed, not raw repository removal. |
| Content stream read/write | 8 | 82% | Hyland Alfresco ACS | - Byte streaming, explicit content headers, multipart Browser Binding create, deduplicating `setContentStream`, no-content compatibility streams, content tombstones, partial body slicing, and offset-zero full-stream classification exist.<br>- Digest verification and governed access exist.<br>- Remaining warning is unsupported append semantics. |
| Object write service | 8 | 72% | Hyland Alfresco ACS | - `createDocument`, `createFolder`, scoped `moveObject`, folder rename, selected standard property updates, custom metadata updates, content stream set/append/delete, `bulkUpdateProperties`, and `createDocumentFromSource` exist.<br>- No broad filing mutation, raw physical delete, checkout/checkin, or policy/item creation.<br>- Delete remains intentionally governed, not raw repository removal. |
| Content stream read/write | 8 | 86% | Hyland Alfresco ACS | - Byte streaming, explicit content headers, multipart Browser Binding create, deduplicating `setContentStream`, whole-object `appendContentStream`, no-content compatibility streams, content tombstones, partial body slicing, and offset-zero full-stream classification exist.<br>- Digest verification and governed access exist.<br>- Chunk-level blob composition remains a later optimization for very large append workloads. |
| Versioning service | 8 | 25% | Hyland Alfresco ACS | - Version properties can be projected from engine versions.<br>- No checkout/checkin/cancelCheckout/PWC services.<br>- No version history route or all-versions query behavior. |
| Discovery/query | 8 | 25% | Hyland Alfresco ACS | - Narrow document select subset exists.<br>- Unsupported joins/order-by return diagnostics.<br>- Missing CMIS SQL predicates, type joins, full-text, ordering, and rich projection rules. |
| Relationships | 5 | 60% | Hyland Alfresco ACS | - Relationship object projection and source filtering exist.<br>- Visibility gates prevent protected relationship leakage.<br>- Missing full relationship service filters, relationship creation through CMIS, and type hierarchy maturity. |
@@ -104,19 +103,19 @@ broad ECM/CMIS replacement surface.
| Renditions | 3 | 15% | Hyland Alfresco ACS | - Native representations could become rendition candidates later.<br>- CMIS rendition capability is currently `none`.<br>- No rendition taxonomy or rendition stream routes. |
| Retention and hold | 2 | 5% | OpenText / Hyland governance stacks | - Native governance metadata can represent intent later.<br>- No CMIS retention/hold model or mutation services.<br>- Explicitly unsupported. |
| Bulk update | 2 | 65% | Hyland Alfresco ACS | - `bulkUpdateProperties` works for the `compat-tck` profile by batching existing property updates with change-token handling.<br>- It is intentionally narrow and not enabled on the normal governed-authoring profile yet.<br>- No advanced partial-success envelope beyond the Browser Binding response list. |
| Browser Binding protocol fidelity | 7 | 78% | Hyland Alfresco ACS | - Browser-style routes, JSON envelopes, CMIS exception names, action aliases, multipart forms, path-addressed root routes, property filters, path segments, and range responses exist.<br>- Selected OpenCMIS Browser Binding repository/type and object/content baseline completes with warnings only.<br>- Optional services and broader CMIS SQL/versioning protocol surfaces remain incomplete. |
| Browser Binding protocol fidelity | 7 | 80% | Hyland Alfresco ACS | - Browser-style routes, JSON envelopes, CMIS exception names, action aliases, multipart forms, path-addressed root routes, property filters, path segments, and range responses exist.<br>- Selected OpenCMIS Browser Binding repository/type and object/content baseline completes with only the local HTTP warning.<br>- Optional services and broader CMIS SQL/versioning protocol surfaces remain incomplete. |
| AtomPub binding | 2 | 0% | Hyland Alfresco ACS | - No AtomPub/XML service document or feeds.<br>- Intentionally deferred until monetized need. |
| Web Services binding | 2 | 0% | Hyland Alfresco ACS | - No SOAP/WSDL stack.<br>- Intentionally deferred until monetized need. |
| External conformance evidence | 3 | 82% | OpenCMIS TCK against Alfresco-like server behavior | - OpenCMIS Browser Binding session creation succeeds against `compat-tck`.<br>- Selected `repository-type` and `object-content` baselines complete with warnings only.<br>- Evidence still covers a selected baseline, not the full OpenCMIS TCK surface. |
| External conformance evidence | 3 | 86% | OpenCMIS TCK against Alfresco-like server behavior | - OpenCMIS Browser Binding session creation succeeds against `compat-tck`.<br>- Selected `repository-type` and `object-content` baselines complete with one local transport warning and no object/content warnings.<br>- Evidence still covers a selected baseline, not the full OpenCMIS TCK surface. |
Weighted result from this table: **59%**.
Weighted result from this table: **60%**.
## Most Important Gaps
1. **External conformance expansion**
- Keep the selected OpenCMIS TCK baseline running against `compat-tck`.
- Treat the current repository/type and object/content warnings as known:
local HTTP and unsupported append.
- Treat the remaining local HTTP warning as a harness/deployment topology
issue, not an adapter behavior failure.
- Expand selected groups after the supported baseline remains stable.
2. **Browser Binding fidelity**
@@ -145,10 +144,11 @@ Weighted result from this table: **59%**.
- Map selected derived representations to CMIS renditions only after we have
stable representation taxonomy and real preview/thumbnail use cases.
7. **Append streams**
- Keep `appendContentStream()` unsupported unless a real client requires it.
- If implemented later, design it through blob composition/deduplication
rather than byte concatenation in the CMIS adapter.
7. **Release transport and operational posture**
- Terminate CMIS access over HTTPS in deployable environments.
- Keep the local HTTP warning accepted only for loopback TCK runs.
- Revisit blob composition if large append workloads become a real usage
pattern.
## Product Positioning Takeaway

View File

@@ -48,7 +48,7 @@ Practical strategy:
| Navigation service | Implemented subset. | `getChildren` and projected parents are supported. `getDescendants`, `getFolderTree`, mutating multifiling, and unfiling are explicitly flagged unsupported. | Low unless full folder tree is required |
| Object service read | Implemented subset. | Object envelopes, allowable actions, content stream descriptors, content stream properties, visibility redaction, and relationship IDs are covered. | Low |
| Object service write | Governed subset. | `createDocument`, custom metadata updates, `setContentStream`, and delete-request lifecycle transition are supported by authoring profiles. Unsupported standard property updates now fail with diagnostics. | Medium |
| Content streams | Implemented subset. | Descriptor and byte-stream routes exist; `setContentStream` writes through deduplicating blob storage. Append/delete content stream are unsupported. | Low |
| Content streams | Implemented subset. | Descriptor and byte-stream routes exist; `setContentStream` and whole-object `appendContentStream` write through deduplicating blob storage, while `deleteContentStream` tombstones the CMIS projection. Chunk-level append composition remains deferred. | Low |
| Versioning | Projection only. | Latest-version properties can be projected from engine versions, but CMIS checkout/PWC/all-versions services are not advertised. | Low if unsupported remains acceptable |
| Discovery/query | Implemented narrow subset. | `SELECT * FROM cmis:document` and `SELECT * FROM kontextual:document` are supported. Joins, order-by, full CMIS SQL predicates, and full-text are flagged unsupported. | Medium |
| Relationships | Implemented subset. | Relationship object projections and source filters are covered and profile-gated. | Low |

View File

@@ -0,0 +1,70 @@
# CMIS OpenCMIS TCK Evidence - Release Readiness - 2026-05-13T22:35:37Z
## Run Summary
- Run ID: `run-20260513T223537Z`
- Local date: 2026-05-14 Europe/Berlin
- Harness: `guide-board` with `open-cmis-tck` extension
- Assessment: `cmis-browser-baseline`
- Target: `kontextual-cmis-compat`
- Endpoint: `http://127.0.0.1:8010/cmis/compat-tck/browser`
- OpenCMIS TCK: CMIS 1.1.0, revision `1789681`
- Result: `completed`
- Policy: `0` unexpected findings, `0` applied waivers
- Run directory: `/tmp/kontextual-cmis-release-20260514-toolchain`
- Internal verification: `.venv/bin/python -m pytest -q` -> `166 passed`,
`14 skipped`
This run was executed after adding Browser Binding `appendContent` /
`appendContentStream` support through the existing content representation
service.
## Normalized Results
| Group | Result | Counts | Remaining non-green findings |
| --- | --- | --- | --- |
| `repository-type` | `warning` | `38 pass`, `2 info`, `1 skipped`, `1 warning` | Local loopback endpoint uses HTTP rather than HTTPS. |
| `object-content` | `pass` | `10 info`, `5 skipped` | None. |
Guide Board summary:
- `pass`: 2
- `warning`: 1
## Score
This is a compatibility-infrastructure score for the selected Browser Binding
baseline, not a CMIS certification score.
| Metric | Score | Basis |
| --- | ---: | --- |
| Selected baseline completion | 100.0% | Guide Board result `completed`; both selected groups returned `0`. |
| Unexpected finding clearance | 100.0% | `0` unexpected findings, `0` fail, `0` infrastructure_error. |
| Warning-adjusted normalized case score | 99.1% | `(56 accepted + 0.5 * 1 warning) / 57 normalized cases`. |
| Strict no-warning normalized case score | 98.2% | `56 accepted / 57 normalized cases`. |
Digest versus `run-20260513T210255Z`:
- OpenCMIS selected-baseline infrastructure score improved from `98.3%` to
`99.1%`.
- `object-content` improved from warning to pass.
- The previous `appendContentStream()` warning is closed.
- The only remaining warning is local HTTP transport in the loopback harness.
## Interpretation
The selected Browser Binding repository/type and object/content baseline is now
release-ready for a controlled preview. The remaining HTTP warning should be
handled as a deployment gate: released access points need HTTPS termination,
while loopback TCK runs may keep the warning as an accepted local harness
condition.
Skipped object/content cases remain aligned with declared capability
boundaries:
- Relationship, policy, and item creation are not advertised as creatable.
- Folder-name change-token subcases are skipped by the TCK.
This evidence still does not cover full CMIS 1.1 certification, AtomPub, Web
Services, PWC/checkin/checkout, full CMIS SQL, renditions, retention, or policy
mutation depth.

View File

@@ -0,0 +1,72 @@
# First Release Readiness
Date: 2026-05-14
Target posture: `0.1.0` controlled preview. This release can be good to go
when it is honest, reproducible, observable, and recoverable. It does not need
to pretend to be a full ECM or certified CMIS repository.
## Release Scope
In scope:
- native asset registry, metadata, relationship, retrieval, transformation,
workflow, service API, blob, S3-backend, and observability foundations,
- profiled CMIS Browser Binding subset for controlled integrations,
- OpenCMIS selected baseline for `repository-type` and `object-content`,
- explicit unsupported flags for out-of-scope CMIS capabilities,
- State Hub registered workplans and evidence documents.
Out of scope for `0.1.0`:
- AtomPub and Web Services bindings,
- full CMIS SQL, checkout/checkin/PWC, policy mutation, retention/hold,
renditions, and broad filing mutation,
- full ECM/records-management semantics,
- formal CMIS certification claim.
## Go / No-Go Gates
| Area | Gate | Current state |
| --- | --- | --- |
| Tests | Full suite passes in the project venv. | `.venv/bin/python -m pytest -q` passed: `166 passed`, `14 skipped`; advisory performance drift warnings recorded. |
| CMIS evidence | OpenCMIS selected baseline completes with no unexpected findings. | `run-20260513T223537Z` completed; only local HTTP warning remains. |
| Transport | Released CMIS access points are served behind HTTPS. | Required deployment gate; local loopback warning is accepted only for harness runs. |
| Capability honesty | Scorecard, unsupported catalog, and examples match behavior. | Updated for `appendContentStream`; final doc review required. |
| Packaging | Version, dependencies, optional extras, and install smoke are checked. | `pyproject.toml` is already `0.1.0`; build/install smoke still required. |
| Configuration | Storage backend, S3 settings, local blob path, and environment defaults are documented. | Existing docs cover backends; release runbook should point to them. |
| Data safety | Blob cleanup, backups, restore path, and migration posture are documented. | Cleanup exists; backup/restore release notes still needed. |
| Security | Actor headers, access-point profiles, secrets, and dependency/license review are done. | Profile model exists; release security review still required. |
| Operations | Health checks, logs, performance history, and known warnings are documented. | Health and performance monitor exist; release runbook still needed. |
| State | State Hub consistency check passes after release docs/workplan updates. | Passed after WP-0015 registration. |
## Known Accepted Limitations
- CMIS Browser Binding is the supported external CMIS protocol surface.
- CMIS multifiling remains projection-only.
- CMIS append is implemented as whole-object append through deduplicating
representation storage; chunk-level blob composition is deferred.
- Local OpenCMIS loopback runs warn about HTTP. This must not be carried into
production-facing access points.
- Performance drift warnings from pytest are advisory unless repeated under a
quiet system baseline; the current run flagged several CMIS API tests.
## Release Procedure
1. Run `.venv/bin/python -m pytest -q`.
2. Run the OpenCMIS selected baseline through `guide-board` and persist the
evidence document.
3. Run State Hub consistency check and ensure workplans are registered.
4. Run packaging smoke: build wheel/sdist and import `kontextual_engine` from a
clean install.
5. Review security/configuration: HTTPS termination, profile exposure, secrets,
local/S3 blob backend settings, and dependency licenses.
6. Update `CHANGELOG.md` or release notes with capabilities, known limitations,
and accepted warnings.
7. Tag the release only after the gates above are green or explicitly waived.
## Release Decision
The current foundation is close to a controlled `0.1.0` preview. The main
remaining release work is not feature depth; it is discipline around repeatable
verification, packaging, deployment posture, and clearly documented limits.

View File

@@ -111,8 +111,8 @@
"service": "object",
"examples": ["source-document", "normalized-representation", "metadata-rich-document", "streamless-document"],
"supported_profiles": ["readonly-browser", "governed-authoring", "admin-export", "compat-tck"],
"must_validate": ["get_object", "get_properties", "get_content_stream", "allowable_actions"],
"unsupported": ["append_content_stream"]
"must_validate": ["get_object", "get_properties", "get_content_stream", "append_content_stream", "allowable_actions"],
"unsupported": []
},
{
"id": "versioning",
@@ -170,7 +170,6 @@
"get_folder_tree": "capability_not_supported",
"multifiling": "projection_only",
"unfiling": "capability_not_supported",
"append_content_stream": "capability_not_supported",
"versioning_services": "capability_not_supported",
"private_working_copy": "capability_not_supported",
"all_versions_search": "capability_not_supported",
@@ -188,22 +187,22 @@
"readonly-browser": {
"must_expose": ["repository-type", "navigation", "object-content", "versioning", "discovery-query", "relationships", "acl-policy", "change-log"],
"must_not_expose_objects": ["doc-confidential-risk"],
"must_reject_actions": ["create_document", "update_properties", "delete_object", "set_content_stream"]
"must_reject_actions": ["create_document", "update_properties", "delete_object", "set_content_stream", "append_content_stream"]
},
"governed-authoring": {
"must_expose": ["repository-type", "navigation", "object-content", "versioning", "discovery-query", "relationships", "acl-policy", "change-log"],
"must_not_expose_objects": ["doc-confidential-risk"],
"must_authorize_actions": ["create_document", "update_properties", "delete_object", "set_content_stream"]
"must_authorize_actions": ["create_document", "update_properties", "delete_object", "set_content_stream", "append_content_stream"]
},
"admin-export": {
"must_expose": ["repository-type", "navigation", "object-content", "versioning", "discovery-query", "relationships", "acl-policy", "change-log", "retention-renditions-bulk"],
"must_not_expose_objects": [],
"must_reject_actions": ["create_document", "update_properties", "delete_object", "set_content_stream"]
"must_reject_actions": ["create_document", "update_properties", "delete_object", "set_content_stream", "append_content_stream"]
},
"compat-tck": {
"must_expose": ["repository-type", "navigation", "object-content", "versioning", "discovery-query", "relationships", "acl-policy", "change-log"],
"must_not_expose_objects": ["doc-confidential-risk"],
"must_authorize_actions": ["create_document", "update_properties", "delete_object", "set_content_stream"]
"must_authorize_actions": ["create_document", "update_properties", "delete_object", "set_content_stream", "append_content_stream"]
}
}
}

View File

@@ -89,6 +89,7 @@ from kontextual_engine.services import (
API_VERSION = "v1"
OPENAPI_VERSION = "1.0.0"
CMIS_APPEND_MAX_COMPOSED_BYTES = 64 * 1024 * 1024
AGENT_OPERATION_CATALOG: tuple[dict[str, Any], ...] = (
@@ -1305,6 +1306,73 @@ class ServiceRuntime:
)
return self.cmis_object(access_point_id, object_id, context)
def cmis_append_content_stream(
self,
access_point_id: str,
object_id: str,
payload: dict[str, Any],
context: OperationContext,
) -> dict[str, Any]:
mapper = self._cmis_mapper(access_point_id)
decision = mapper.access_point.decide_action(CMISAction.SET_CONTENT_STREAM, context, resource=object_id)
if not decision.allowed:
raise _cmis_authorization_error(decision, "appendContentStream")
asset_id = _cmis_asset_id(object_id)
asset = self.repository.get_asset(asset_id)
if not mapper.access_point.exposes_asset(asset, context):
raise NotFoundError(
"CMIS object not found",
details={"object_id": object_id, "access_point_id": access_point_id},
)
expected = (
payload.get("expected_current_version_id")
or payload.get("changeToken")
or payload.get("change_token")
)
self._cmis_assert_change_token(asset, expected, operation="appendContentStream")
appended = _cmis_payload_bytes(payload.get("content", b""))
is_last_chunk = _cmis_form_bool(payload.get("isLastChunk") or payload.get("is_last_chunk"), default=True)
base = b""
kind = payload.get("kind", RepresentationKind.SOURCE.value)
media_type = _cmis_media_type(payload.get("media_type", "application/octet-stream"))
if self._cmis_asset_representations(asset):
stream = self.content_service().get_content_stream(asset_id, context)
base = stream.content
kind = stream.representation.kind.value
media_type = _cmis_media_type(payload.get("media_type") or stream.representation.media_type)
elif asset.metadata.get("cmis_content_deleted"):
metadata = dict(asset.metadata)
metadata.pop("cmis_content_deleted", None)
self.repository.save_asset(replace(asset, metadata=metadata))
composed_size = len(base) + len(appended)
if composed_size > CMIS_APPEND_MAX_COMPOSED_BYTES:
raise ValidationError(
"CMIS append content stream exceeds the composed append limit",
details={
"code": "cmis.append_content_limit_exceeded",
"cmis_exception": "constraint",
"operation": "appendContentStream",
"max_size_bytes": CMIS_APPEND_MAX_COMPOSED_BYTES,
"composed_size_bytes": composed_size,
},
)
self.content_service().add_representation_from_bytes(
asset_id,
kind,
media_type,
base + appended,
context,
expected_current_version_id=expected,
metadata={
"cmis": {
"operation": "appendContentStream",
"is_last_chunk": is_last_chunk,
"appended_bytes": len(appended),
}
},
)
return self.cmis_object(access_point_id, object_id, context)
def cmis_delete_content_stream(
self,
access_point_id: str,
@@ -3676,6 +3744,11 @@ def create_app(runtime: ServiceRuntime | None = None):
raise ValidationError("CMIS object id is required", details={"operation": "setContentStream"})
response(runtime.cmis_set_content_stream, access_point_id, object_id, payload, context)
return response(runtime.cmis_browser_object, access_point_id, object_id, context)
if action in {"appendContentStream", "appendContent"}:
if not object_id:
raise ValidationError("CMIS object id is required", details={"operation": "appendContentStream"})
response(runtime.cmis_append_content_stream, access_point_id, object_id, payload, context)
return response(runtime.cmis_browser_object, access_point_id, object_id, context)
if action in {"deleteContent", "deleteContentStream"}:
if not object_id:
raise ValidationError("CMIS object id is required", details={"operation": "deleteContentStream"})
@@ -3699,6 +3772,8 @@ def create_app(runtime: ServiceRuntime | None = None):
"move",
"setContentStream",
"setContent",
"appendContentStream",
"appendContent",
"deleteContent",
"deleteContentStream",
],
@@ -4632,6 +4707,24 @@ def _cmis_media_type(value: Any) -> str:
return media_type or "application/octet-stream"
def _cmis_payload_bytes(value: Any) -> bytes:
if value is None:
return b""
if isinstance(value, bytes):
return value
if isinstance(value, bytearray):
return bytes(value)
return str(value).encode("utf-8")
def _cmis_form_bool(value: Any, *, default: bool = False) -> bool:
if value in (None, ""):
return default
if isinstance(value, bool):
return value
return str(value).strip().lower() in {"1", "true", "yes", "on"}
def _cmis_value_list(value: Any) -> list[str]:
if value is None or value == "":
return []

View File

@@ -191,11 +191,6 @@ UNSUPPORTED_FEATURES: dict[str, dict[str, Any]] = {
"standard_flag": "capability_join",
},
"order_by": {"status": "unsupported", "reason": "query_not_supported", "standard_flag": "capability_order_by"},
"append_content_stream": {
"status": "unsupported",
"reason": "capability_not_supported",
"standard_flag": "capability_content_stream_updatability",
},
"apply_acl": {"status": "unsupported", "reason": "operation_not_implemented", "standard_flag": "capability_acl"},
"apply_policy": {"status": "unsupported", "reason": "capability_not_supported"},
"remove_policy": {"status": "unsupported", "reason": "capability_not_supported"},

View File

@@ -366,6 +366,54 @@ def test_cmis_browser_binding_delete_content_stream_tombstones_content(cmis_clie
assert content_after_delete.json()["exception"] == "constraint"
def test_cmis_browser_binding_append_content_stream_adds_versioned_content(cmis_client) -> None:
document = cmis_client.post(
"/cmis/compat-tck/browser/root",
data={
"cmisaction": "createDocument",
"propertyId[0]": "cmis:objectTypeId",
"propertyValue[0]": "cmis:document",
"propertyId[1]": "cmis:name",
"propertyValue[1]": "Append Content Document",
},
files={"content": ("append-content.txt", b"one", "text/plain")},
).json()
object_id = document["properties"]["cmis:objectId"]["value"]
token = document["properties"]["cmis:changeToken"]["value"]
appended = cmis_client.post(
"/cmis/compat-tck/browser/root",
data={
"cmisaction": "appendContent",
"objectId": object_id,
"changeToken": token,
"isLastChunk": "true",
},
files={"content": ("append-content.txt", b" two", "text/plain")},
)
content = cmis_client.get(
"/cmis/compat-tck/browser/root",
params={"cmisselector": "content", "objectId": object_id},
)
stale_append = cmis_client.post(
"/cmis/compat-tck/browser/root",
data={
"cmisaction": "appendContentStream",
"objectId": object_id,
"changeToken": token,
},
files={"content": ("append-content.txt", b" stale", "text/plain")},
)
assert appended.status_code == 200
assert appended.json()["properties"]["cmis:contentStreamLength"]["value"] == 7
assert appended.json()["properties"]["cmis:contentStreamMimeType"]["value"] == "text/plain"
assert appended.json()["properties"]["cmis:changeToken"]["value"] != token
assert content.content == b"one two"
assert stale_append.status_code == 409
assert stale_append.json()["exception"] == "updateConflict"
def test_cmis_browser_binding_change_tokens_conflict_on_stale_updates(cmis_client) -> None:
document = cmis_client.post(
"/cmis/compat-tck/browser/root",

View File

@@ -80,6 +80,7 @@ def test_profile_visibility_and_mutation_expectations_are_explicit() -> None:
"update_properties",
"delete_object",
"set_content_stream",
"append_content_stream",
]
else:
assert expectations["must_reject_actions"] == [
@@ -87,6 +88,7 @@ def test_profile_visibility_and_mutation_expectations_are_explicit() -> None:
"update_properties",
"delete_object",
"set_content_stream",
"append_content_stream",
]
@@ -117,4 +119,3 @@ def test_admin_export_is_the_only_profile_for_deferred_extension_group() -> None
for profile_name, expectations in catalog["profile_expectations"].items():
exposes_extension = "retention-renditions-bulk" in expectations["must_expose"]
assert exposes_extension is (profile_name == "admin-export")

View File

@@ -27,6 +27,7 @@ ACTION_MAP = {
"update_properties": CMISAction.UPDATE_PROPERTIES,
"delete_object": CMISAction.DELETE_OBJECT,
"set_content_stream": CMISAction.SET_CONTENT_STREAM,
"append_content_stream": CMISAction.SET_CONTENT_STREAM,
}
@@ -119,4 +120,3 @@ def test_optional_tck_result_template_can_capture_gap_mapping() -> None:
assert template["summary"] == {"passed": 0, "failed": 0, "skipped": 0, "known_gap": 0}
assert "capability_gaps" in template
assert template["groups"][0]["status"] == "not_run"

View File

@@ -10,7 +10,7 @@ topic_slug: markitect
planning_priority: high
planning_order: 14
created: "2026-05-08"
updated: "2026-05-13"
updated: "2026-05-14"
state_hub_workstream_id: "ccfa90ee-be23-499b-a727-451a0d289df7"
---
@@ -226,12 +226,20 @@ Latest verification:
- `repository-type`: `38 pass`, `2 info`, `1 skipped`, `1 warning`.
- `object-content`: `10 info`, `5 skipped`, `1 warning`.
Release-warning pass:
- OpenCMIS: `run-20260513T223537Z` in
`/tmp/kontextual-cmis-release-20260514-toolchain`.
- Guide Board status: `completed`, with `0` unexpected findings.
- `repository-type`: `38 pass`, `2 info`, `1 skipped`, `1 warning`.
- `object-content`: `10 info`, `5 skipped`, `0 warning`.
- Warning-adjusted selected-baseline score improved from `98.3%` to `99.1%`.
Current external frontier:
- The selected OpenCMIS Browser Binding repository/type and object/content
baseline is completed with warnings only.
- Remaining warnings are local HTTP instead of HTTPS and unsupported
`appendContentStream()`.
- The only remaining warning is local HTTP instead of HTTPS.
- Further maturity work should move to deliberately selected optional CMIS
areas: query depth, descendants/folder-tree read navigation, version-history
reads, relationship service depth, ACL detail, and rendition projection.
@@ -338,7 +346,9 @@ Progress:
`Content-Length`, `Content-Type`, `ETag`, and `Content-Range`.
- Done for `deleteContentStream` tombstone semantics and offset-zero range
classification.
- `appendContentStream()` remains explicitly unsupported by design.
- Done for Browser Binding `appendContent` / `appendContentStream` as
whole-object append through the deduplicating representation service, with a
composed-size guard.
## D14.6 - Add natural navigation and query depth