Compare commits

...
14 Commits
Author SHA1 Message Date
james.maes 4e150dd61a Merge pull request 'ci: use ci-workflows@v1 again' (#16) from ci/pin-v1 into main
node / node (push) Successful in 37s
build / node (push) Successful in 37s
2026-11-08 14:27:46 +00:00
james.maesandClaude Opus 5.5 92b78bc996 ci: use ci-workflows@v1 again
node / node (push) Successful in 36s
build / node (push) Successful in 36s
v1 now contains node.yaml publishing (ci-workflows 0e0f145).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 14:25:28 +00:00
james.maes a3819b469c Merge pull request 'ci: Gitea CI publishes the release from the tag; 1.1.1-rc.1' (#15) from ci/gitea-release into main
node / node (push) Successful in 42s
build / node (push) Successful in 42s
2026-11-08 13:37:58 +00:00
james.maesandClaude Opus 5.5 09fdb0663c ci: Gitea CI publishes the release from the tag; 1.1.1-rc.1
node / node (push) Successful in 1m15s
build / node (push) Successful in 1m15s
build.yaml sets publish: true with release-files dist/* on the shared node.yaml
(@main until ci-workflows v1 moves): a v<package.json version> tag reruns every
check, bundles, and creates the Gitea release with the archive, SHA256SUMS and
the CHANGELOG section. Replaces the retired GitHub release + attestation path
(Gitea has no attestation equivalent). 1.1.1-rc.1 is the pipeline proof, with
no contract change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:27:55 -06:00
james.maes 39e4c3eee7 Merge pull request 'ci: first run on ci-workflows v1' (#14) from ci/first-run-v1 into main
node / node (push) Successful in 1m10s
build / node (push) Successful in 1m10s
2026-11-07 22:41:46 +00:00
James MaesandClaude Opus 5.5 80e859534e ci: first run on ci-workflows v1 (node.yaml)
node / node (push) Successful in 40s
build / node (push) Successful in 40s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:37:56 -06:00
james.maes eee42e951d Merge pull request 'ci: build on the shared ci-workflows node.yaml, remove GitHub workflows' (#13) from ci/ci-workflows into main 2026-11-07 22:16:00 +00:00
james.maesandClaude Opus 5.5 177db4b06b ci: pin ci-workflows node.yaml to @v1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:15:36 -06:00
james.maesandClaude Opus 5.5 57670e9891 ci: build on the shared ci-workflows node.yaml, remove GitHub workflows
node / node (push) Successful in 1m13s
build / node (push) Successful in 1m13s
Gitea CI runs the uv script-quality and source-header checks, typecheck,
lint, check and bundle through KofTwentyTwo/ci-workflows node.yaml (Node 24,
pinned uv). Removes the retired GitHub Actions workflows (ci, codeql,
codeql-analysis, pr-gates, security) and the CodeQL-only scripts. The EX-0015
uri-js guard keeps its lock/integrity/license pins; its check of the
pr-gates.yml allowlist entry goes with that file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:14:09 -06:00
james.maes 220cd529f1 Merge pull request 'docs(release): record protocol 1.1.0 publication and consumer fixture pins' (#12) from docs/release-1.1.0-20261105 into main
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
QRun-IO/ci-policy: Shared Node CI / secrets (push) Successful in 14s
QRun-IO/ci-policy: Shared Node CI / verify (push) Successful in 41s
QRun-IO/ci-policy: Shared Node CI / gate (push) Successful in 13s
QRun-IO/ci-policy: ci / node-8 (push) Successful in 55s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 5s
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 10s
QRun-IO/ci-policy: native / native-result (push) Successful in 6s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
2026-11-06 14:15:02 +00:00
james.maesandClaude Fable 5.1 fde9cd8183 docs(versions): 1.2.1 client in the negotiation example
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: Shared Node CI / secrets (pull_request) Successful in 16s
QRun-IO/ci-policy: Shared Node CI / verify (pull_request) Successful in 47s
QRun-IO/ci-policy: Shared Node CI / gate (pull_request) Successful in 13s
QRun-IO/ci-policy: ci / node-8 (pull_request) Successful in 1m4s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 6s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-06 07:55:33 -06:00
james.maesandClaude Fable 5.1 920dd57035 docs: the paired Windows connector is 1.2.1 (1.2.0 was never published)
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Canceled after 0s
QRun-IO/ci-policy: Shared Node CI / secrets (pull_request) Canceled after 0s
QRun-IO/ci-policy: Shared Node CI / gate (pull_request) Canceled after 0s
QRun-IO/ci-policy: native / native-result (pull_request) Canceled after 0s
QRun-IO/ci-policy: Shared Node CI / verify (pull_request) Successful in 49s
QRun-IO/ci-policy: ci / node-8 (pull_request) Canceled after 50s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-06 07:55:04 -06:00
james.maesandClaude Fable 5.1 54c02db3a0 docs(readme): published distribution 1.1.0 and the 2.4.0 cohort
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: Shared Node CI / secrets (pull_request) Successful in 11s
QRun-IO/ci-policy: Shared Node CI / gate (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / ci-result (pull_request) Canceled after 0s
QRun-IO/ci-policy: Shared Node CI / verify (pull_request) Canceled after 38s
QRun-IO/ci-policy: ci / node-8 (pull_request) Canceled after 38s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 7s
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-06 07:54:15 -06:00
james.maesandClaude Fable 5.1 a4ab8a4fa1 docs(release): record protocol 1.1.0 publication and consumer fixture pins
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow d20a578c7d818bc7ca32a2ac2c675da4e4f27385] (pull_request) Failing after 2s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Failing after 1s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Waiting to run
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-05 17:09:15 -06:00
18 changed files with 61 additions and 564 deletions

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
name: build
on:
push:
jobs:
node:
# @main until the owner moves ci-workflows v1 onto the publishing node.yaml; then back to @v1.
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/node.yaml@v1
with:
node-version: '24'
install: npm ci --ignore-scripts
uv: true
commands: |
uv sync --locked
uv run --locked --no-sync python .github/scripts/test-script-quality.py
uv run --locked --no-sync python .github/scripts/check-script-quality.py
uv run --locked --no-sync python .github/scripts/test-source-headers.py
uv run --locked --no-sync python .github/scripts/check-source-headers.py
npm run typecheck
npm run lint
npm run check
npm run bundle
# A v<package.json version> tag creates the Gitea release with the bundle, SHA256SUMS and the
# CHANGELOG section (-rc.N -> prerelease). The package is private: nothing goes to npm.
publish: true
npm-publish-dirs: ''
release-files: dist/*
-108
View File
@@ -1,108 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
"""Reject missing/malformed CodeQL evidence before enforcing the severity gate."""
import json
import math
import os
from pathlib import Path
from typing import TypedDict
RuleProperties = TypedDict("RuleProperties", {"security-severity": str | float | None}, total=False)
class Rule(TypedDict):
"""Keep the rule identity and optional severity metadata used by the gate."""
id: str
properties: RuleProperties
class Component(TypedDict, total=False):
"""Describe CodeQL driver or extension rule metadata."""
name: str
rules: list[Rule]
class Tool(TypedDict, total=False):
"""Group the driver and optional extension rule catalogs."""
driver: Component
extensions: list[Component]
class Result(TypedDict, total=False):
"""Describe only the result fields consumed by the severity gate."""
ruleId: str
rule: dict[str, str]
suppressions: list[object]
class Run(TypedDict, total=False):
"""Retain invocation success and findings for one analyzed language."""
tool: Tool
results: list[Result]
invocations: list[dict[str, bool]]
class Report(TypedDict, total=False):
"""Represent SARIF fields while retaining the explicit runtime validation."""
version: str
runs: list[Run]
MAX_SEVERITY = 10
def check(directory: str | Path, language: str, threshold: float) -> bool: # noqa: PLR0912 -- Keep the ordered evidence checks together; none may be skipped.
"""Validate complete CodeQL evidence and report findings at the threshold."""
if not math.isfinite(threshold) or not 0 <= threshold <= MAX_SEVERITY:
raise ValueError("Invalid security severity threshold")
canonical = {"java-kotlin": "java", "javascript-typescript": "javascript", "c-cpp": "cpp"}.get(language, language)
files = sorted(Path(directory).glob("*.sarif"))
if not canonical or not any(path.name == canonical + ".sarif" for path in files):
raise ValueError("Missing expected CodeQL SARIF report for " + language)
blocking: list[tuple[float, str]] = []
for path in files:
report: Report = json.loads(path.read_text(encoding="utf-8"))
runs = report.get("runs")
if report.get("version") != "2.1.0" or not isinstance(runs, list) or not runs:
raise ValueError("Invalid or empty SARIF runs: " + path.name)
for run in runs:
driver = run.get("tool", {}).get("driver", {})
results = run.get("results")
if driver.get("name") != "CodeQL" or not isinstance(driver.get("rules"), list) or not isinstance(results, list):
raise ValueError("Missing CodeQL tool/rules/results evidence: " + path.name)
rules: dict[str, Rule] = {}
for component in [driver, *run.get("tool", {}).get("extensions", [])]:
for rule in component.get("rules", []):
rules[rule["id"]] = rule
if not rules:
raise ValueError("CodeQL report has no analyzed rule metadata: " + path.name)
for invocation in run.get("invocations", []):
if invocation.get("executionSuccessful") is False:
raise ValueError("CodeQL invocation failed: " + path.name)
for result in results:
rule_id = result.get("ruleId") or result.get("rule", {}).get("id")
if rule_id not in rules:
raise ValueError("Result references missing rule metadata: " + path.name)
if result.get("suppressions"):
continue
severity = float(rules[rule_id].get("properties", {}).get("security-severity", 0) or 0)
if not math.isfinite(severity) or not 0 <= severity <= MAX_SEVERITY:
raise ValueError("Invalid rule severity: " + rule_id)
if severity >= threshold:
blocking.append((severity, rule_id))
for severity, rule_id in sorted(blocking, reverse=True):
print(f"::error title=CodeQL security finding::{rule_id} ({severity})")
print(f"Validated {len(files)} SARIF report(s); {len(blocking)} blocking result(s)")
return bool(blocking)
if __name__ == "__main__":
raise SystemExit(1 if check(os.environ["SARIF_DIR"], os.environ["EXPECTED_LANGUAGE"], float(os.environ["THRESHOLD"])) else 0)
+1 -8
View File
@@ -5,7 +5,6 @@
import hashlib
import json
import re
import shutil
import subprocess
import sys
@@ -16,12 +15,10 @@ PYTHON_SUFFIXES = (".py", ".pyi", ".pyw")
SCRIPT_PATTERN = ".github/scripts/*.py"
PYRIGHT_CONFIG = ".github"
JAVASCRIPT_SUFFIXES = (".js", ".cjs", ".mjs", ".jsx", ".ts", ".mts", ".cts", ".tsx")
# EX-0015: the only npm package whose license classification the PR gate may exempt (the matcher ignores versions, so this guard pins it).
URI_JS_PURL = "pkg:npm/uri-js"
# EX-0015: the only npm package with a license exception; this guard pins the reviewed copy.
URI_JS_VERSION = "4.4.1"
URI_JS_INTEGRITY = "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="
URI_JS_LICENSE_SHA256 = "0af366eff4c01ec147c9c61ea9e8ffad64a4294754c9d79355f3fd1b97cb2fb9"
PR_GATES = ".github/workflows/pr-gates.yml"
# Microsoft's official npm Pyright, restored from package-lock.json by `npm ci --ignore-scripts`.
PYRIGHT_ENTRY = Path(__file__).resolve().parents[2] / "node_modules" / "pyright" / "index.js"
@@ -116,10 +113,6 @@ def check_uri_js_exception(root: Path) -> None:
digest = hashlib.sha256((root / "node_modules" / "uri-js" / "LICENSE").read_bytes()).hexdigest()
if digest != URI_JS_LICENSE_SHA256:
raise ValueError(f"Installed uri-js LICENSE bytes differ from the reviewed EX-0015 copy ({digest})")
caller = [line for line in (root / PR_GATES).read_text(encoding="utf-8").splitlines() if not line.lstrip().startswith("#")]
listed = re.findall(r"pkg:npm/[^\s,]+", "\n".join(caller))
if listed != [URI_JS_PURL]:
raise ValueError(f"EX-0015 permits only {URI_JS_PURL} in allow-dependencies-licenses, found {listed}")
def main() -> None:
-107
View File
@@ -1,107 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
"""Exercise the exact CI guards using synthetic evidence only."""
import contextlib
import copy
import importlib.util
import io
import json
import sys
import tempfile
import unittest
from pathlib import Path
from types import ModuleType
from typing import Any, override
sys.dont_write_bytecode = True
def load(name: str) -> ModuleType:
"""Load the real sibling guard for synthetic evidence tests."""
spec = importlib.util.spec_from_file_location(name, Path(__file__).with_name(name + ".py"))
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
codeql = load("check-codeql-sarif")
class SarifTests(unittest.TestCase):
"""Reject incomplete SARIF evidence and preserve severity enforcement."""
@override
def setUp(self) -> None:
"""Create isolated synthetic evidence with cleanup after each test."""
self.temporary = tempfile.TemporaryDirectory()
self.addCleanup(self.temporary.cleanup)
self.directory = Path(self.temporary.name)
self.report: dict[str, Any] = {"version": "2.1.0", "runs": [{"tool": {"driver": {"name": "CodeQL", "rules": [{"id": "synthetic", "properties": {"security-severity": "8.0"}}]}}, "results": []}]}
def save(self, report: object) -> None:
"""Write the test body as SARIF without validating it first."""
(self.directory / "java.sarif").write_text(json.dumps(report), encoding="utf-8")
def test_clean_report_with_zero_findings_passes(self) -> None:
"""Verify clean report with zero findings passes."""
self.save(self.report)
self.assertFalse(codeql.check(self.directory, "java-kotlin", 7))
def test_missing_report_or_wrong_language_fails(self) -> None:
"""Verify missing report or wrong language fails."""
with self.assertRaises(ValueError):
codeql.check(self.directory, "java-kotlin", 7)
self.save(self.report)
with self.assertRaises(ValueError):
codeql.check(self.directory, "csharp", 7)
def test_empty_or_malformed_report_fails(self) -> None:
"""Verify empty or malformed report fails."""
for text in ["", "{", "null", "[]"]:
with self.subTest(text=text):
(self.directory / "java.sarif").write_text(text)
with self.assertRaises((ValueError, AttributeError)):
codeql.check(self.directory, "java-kotlin", 7)
def test_missing_runs_tool_rules_or_results_fails(self) -> None:
"""Verify missing runs tool rules or results fails."""
variants: list[dict[str, Any]] = [{"version": "2.1.0"}, {"version": "2.1.0", "runs": []}]
for field in ["tool", "results"]:
bad = copy.deepcopy(self.report)
del bad["runs"][0][field]
variants.append(bad)
bad = copy.deepcopy(self.report)
bad["runs"][0]["tool"]["driver"]["rules"] = []
variants.append(bad)
for bad in variants:
with self.subTest(report=bad):
self.save(bad)
with self.assertRaises(ValueError):
codeql.check(self.directory, "java-kotlin", 7)
def test_high_finding_blocks_and_unknown_rule_fails(self) -> None:
"""Verify high finding blocks and unknown rule fails."""
self.report["runs"][0]["results"] = [{"ruleId": "synthetic"}]
self.save(self.report)
with contextlib.redirect_stdout(io.StringIO()):
self.assertTrue(codeql.check(self.directory, "java-kotlin", 7))
self.report["runs"][0]["results"] = [{"ruleId": "missing"}]
self.save(self.report)
with self.assertRaises(ValueError):
codeql.check(self.directory, "java-kotlin", 7)
def test_failed_invocation_and_invalid_threshold_fail(self) -> None:
"""Verify failed invocation and invalid threshold fail."""
self.report["runs"][0]["invocations"] = [{"executionSuccessful": False}]
self.save(self.report)
with self.assertRaises(ValueError):
codeql.check(self.directory, "java-kotlin", 7)
with self.assertRaises(ValueError):
codeql.check(self.directory, "java-kotlin", float("nan"))
if __name__ == "__main__":
unittest.main()
+8 -24
View File
@@ -234,37 +234,29 @@ class JavaScriptProbeTests(unittest.TestCase):
self.assertIn("TS2322", bad.stdout)
URI_GATES = "jobs:\n pr:\n with:\n allow-dependencies-licenses: pkg:npm/uri-js\n"
URI_MIXED_GATES = "jobs:\n pr:\n with:\n allow-dependencies-licenses: >-\n pkg:nuget/Microsoft.WindowsAppSDK,\n pkg:npm/uri-js\n"
def uri_lock(version: str = "4.4.1", integrity: str | None = None, dev: bool = True, direct: bool = False, consumer_dev: bool = True) -> dict[str, Any]:
"""Build a minimal package-lock shape with uri-js under an ESLint-like consumer."""
root_entry: dict[str, Any] = {"devDependencies": {"uri-js": version} if direct else {"eslint": "10.14.0"}}
return {"packages": {"": root_entry, "node_modules/uri-js": {"version": version, "integrity": integrity or guard.URI_JS_INTEGRITY, "dev": dev}, "node_modules/eslint": {"dev": consumer_dev, "dependencies": {"uri-js": "^4.2.2"}}}}
def uri_project(root: Path, lock: dict[str, Any], gates: str = URI_GATES, license_bytes: bytes | None = None) -> None:
"""Write a private project with the lock, installed LICENSE and PR caller that the guard inspects."""
def uri_project(root: Path, lock: dict[str, Any], license_bytes: bytes | None = None) -> None:
"""Write a private project with the lock and installed LICENSE that the guard inspects."""
(root / "package-lock.json").write_text(json.dumps(lock), encoding="utf-8")
installed = root / "node_modules" / "uri-js"
installed.mkdir(parents=True)
installed.joinpath("LICENSE").write_bytes((ROOT / "node_modules" / "uri-js" / "LICENSE").read_bytes() if license_bytes is None else license_bytes)
workflows = root / ".github" / "workflows"
workflows.mkdir(parents=True)
(workflows / "pr-gates.yml").write_text("# EX-0015 pkg:npm/uri-js is cited here\n" + gates, encoding="utf-8")
class UriJsExceptionTests(unittest.TestCase):
"""Allow the EX-0015 license exception only for the reviewed, unmodified, development-only uri-js."""
def test_reviewed_uri_js_with_exact_caller_is_accepted(self) -> None:
"""Accept the reviewed package, installed license bytes and an exact caller list, mixed with NuGet entries."""
for gates in (URI_GATES, URI_MIXED_GATES):
with tempfile.TemporaryDirectory() as name, self.subTest(gates=gates):
root = Path(name)
uri_project(root, uri_lock(), gates)
guard.check_uri_js_exception(root)
def test_reviewed_uri_js_is_accepted(self) -> None:
"""Accept the reviewed package with the reviewed installed license bytes."""
with tempfile.TemporaryDirectory() as name:
root = Path(name)
uri_project(root, uri_lock())
guard.check_uri_js_exception(root)
def test_other_version_integrity_scope_or_consumer_is_rejected(self) -> None:
"""Reject another version, integrity, non-dev or direct use, a non-dev consumer and a second copy."""
@@ -284,14 +276,6 @@ class UriJsExceptionTests(unittest.TestCase):
with self.assertRaisesRegex(ValueError, "LICENSE bytes differ"):
guard.check_uri_js_exception(root)
def test_caller_must_list_only_uri_js(self) -> None:
"""Reject a caller that lists no npm exception, another package, a versioned form or a duplicate."""
for gates in ("jobs:\n pr:\n uses: shared\n", URI_GATES.rstrip() + ", pkg:npm/other\n", "jobs:\n pr:\n with:\n allow-dependencies-licenses: pkg:npm/other\n", URI_GATES.rstrip() + "@4.4.1\n", URI_GATES.rstrip() + ", pkg:npm/uri-js\n"):
with tempfile.TemporaryDirectory() as name, self.subTest(gates=gates), self.assertRaisesRegex(ValueError, "permits only"):
root = Path(name)
uri_project(root, uri_lock(), gates)
guard.check_uri_js_exception(root)
if __name__ == "__main__":
unittest.main()
+1
View File
@@ -3,6 +3,7 @@
"includes": [
".github/*.yml",
".github/*.yaml",
".gitea/*.yaml",
".github/scripts/*.py",
"tools/*.mjs"
],
-100
View File
@@ -1,100 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
name: Protocol CI and release
on:
pull_request:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: '24'
package-manager-cache: false
- run: npm ci --ignore-scripts
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: '0.13.6'
enable-cache: false
- name: Restore locked Python tools
run: uv sync --locked
- name: Verify script quality gates and rejection paths
run: |
uv run --locked --no-sync python .github/scripts/test-script-quality.py
uv run --locked --no-sync python .github/scripts/check-script-quality.py
npm run typecheck
npm run lint
- name: Verify owned source notices and rejection paths
run: |
python3 .github/scripts/test-source-headers.py
python3 .github/scripts/check-source-headers.py
- run: npm run check
- run: npm run bundle
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: protocol-bundle
path: dist/*
if-no-files-found: error
release-security:
if: startsWith(github.ref, 'refs/tags/v')
uses: ./.github/workflows/security.yml
permissions:
contents: read
security-events: write
release-codeql:
if: startsWith(github.ref, 'refs/tags/v')
uses: ./.github/workflows/codeql.yml
permissions:
contents: read
security-events: write
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [verify, release-security, release-codeql]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: protocol-bundle
path: dist
- name: Verify packaged checksums
working-directory: dist
run: sha256sum --check --strict SHA256SUMS
- name: Attest verified release artifacts
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-path: dist/*
- name: Publish versioned GitHub release assets
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
prerelease=()
if [[ "$GITHUB_REF_NAME" == *-* ]]; then prerelease+=(--prerelease); fi
gh release create "$GITHUB_REF_NAME" dist/*.tar.gz dist/SHA256SUMS \
--verify-tag --title "$GITHUB_REF_NAME" --notes-file CHANGELOG.md "${prerelease[@]}"
-128
View File
@@ -1,128 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
# Local copy of KofTwentyTwo/standards at fdf5ed029603c938615a32b1beca3628b8787945 (v0.1.2).
# Pins, inputs and analysis checks preserved; report validation now fails closed.
# Static analysis (K22-CI-22): CodeQL with the security-extended suite, failing on
# alerts of high severity or above. Called as:
#
# jobs:
# codeql:
# uses: KofTwentyTwo/standards/.github/workflows/codeql.yml@<sha> # vX.Y.Z
# permissions:
# contents: read
# security-events: write
# with:
# languages: '["csharp", "actions"]'
# build-mode: manual
# build-command: dotnet build MySolution.slnx -p:Platform=x64
# runs-on: windows-latest
#
# producing `codeql / analyze (<language>)` for each language. The CodeQL action
# uploads results but never fails a build on them, so the last step reads the SARIF
# and fails on any result whose rule has security-severity >= the threshold.
# Suppress a false positive with a query filter in a CodeQL config file passed as
# `config-file`, citing the exception record in a comment.
name: codeql
on:
workflow_call:
inputs:
languages:
description: JSON array of CodeQL languages, e.g. '["csharp","actions"]'.
type: string
required: true
build-mode:
description: CodeQL build mode for compiled languages (none, autobuild, manual). Interpreted languages always use none.
type: string
default: autobuild
build-command:
description: Command run for build-mode manual.
type: string
default: ""
runs-on:
description: Runner label; Windows-only builds (WinUI, WPF) need windows-latest.
type: string
default: ubuntu-24.04
setup-dotnet:
description: Install the .NET SDK from global.json before building.
type: boolean
default: false
config-file:
description: Optional path to a CodeQL configuration file in the calling repository.
type: string
default: ""
fail-on-security-severity:
description: Minimum CVSS-style security-severity that fails the job (7.0 = high).
type: string
default: "7.0"
permissions: {}
jobs:
analyze:
name: analyze (${{ matrix.language }})
runs-on: ${{ inputs.runs-on }}
timeout-minutes: 60
permissions:
contents: read
security-events: write # upload SARIF results to code scanning
strategy:
fail-fast: false
matrix:
language: ${{ fromJSON(inputs.languages) }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Test scan guard rejection paths
shell: python
run: |
import runpy
runpy.run_path(".github/scripts/test-scan-gates.py", run_name="__main__")
- name: Set up .NET SDK
if: inputs.setup-dotnet
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Initialize CodeQL
uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
languages: ${{ matrix.language }}
build-mode: ${{ contains(fromJSON('["actions","javascript-typescript","python","ruby"]'), matrix.language) && 'none' || inputs.build-mode }}
queries: security-extended
config-file: ${{ inputs.config-file }}
- name: Build (manual mode)
if: inputs.build-mode == 'manual' && !contains(fromJSON('["actions","javascript-typescript","python","ruby"]'), matrix.language)
shell: bash
env:
BUILD_COMMAND: ${{ inputs.build-command }}
run: |
if [[ -z "$BUILD_COMMAND" ]]; then
echo "::error::build-mode is manual but build-command is empty"
exit 1
fi
eval "$BUILD_COMMAND"
- name: Analyze
uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
category: /language:${{ matrix.language }}
output: codeql-sarif
# Fork pull requests have a read-only token; analyze still runs and gates.
upload: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) && 'never' || 'always' }}
- name: Gate on security severity
shell: python
env:
SARIF_DIR: codeql-sarif
THRESHOLD: ${{ inputs.fail-on-security-severity }}
EXPECTED_LANGUAGE: ${{ matrix.language }}
run: |
import runpy
runpy.run_path(".github/scripts/check-codeql-sarif.py", run_name="__main__")
-21
View File
@@ -1,21 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
name: codeql
on:
pull_request:
push:
branches: [main]
schedule:
- cron: '41 6 * * 1'
workflow_call:
permissions: {}
jobs:
codeql:
uses: ./.github/workflows/codeql-analysis.yml
permissions:
contents: read
security-events: write
with:
languages: '["javascript-typescript", "actions"]'
build-mode: none
-29
View File
@@ -1,29 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
# Pull request gates (K22-CI-01): Conventional Commit title, DCO sign-off, and
# dependency review, from the shared KofTwentyTwo/standards workflow pinned by SHA
# (K22-CI-11). Dependabot's github-actions ecosystem keeps the pin current.
name: pr-gates
on:
pull_request:
permissions: {}
concurrency:
group: pr-gates-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
pr:
uses: KofTwentyTwo/standards/.github/workflows/pr.yml@fdf5ed029603c938615a32b1beca3628b8787945 # v0.1.2
with:
# EX-0015: development-only uri-js 4.4.1 (ESLint transitive; BSD-2-Clause plus an authors' views sentence).
# https://git.kof22.com/KofTwentyTwo/standards/src/commit/a18179e90f8e6d35ba9f5e583249885c066e52b8/exceptions/register.md#ex-0015
# The matcher ignores versions; check-script-quality.py pins the version, lock integrity and installed
# license bytes and rejects any other pkg:npm entry here.
allow-dependencies-licenses: pkg:npm/uri-js
permissions:
contents: read # read the commits being merged
pull-requests: read # read the pull request title and dependency diff
-26
View File
@@ -1,26 +0,0 @@
# Copyright 2026 Kingsrook, LLC
# SPDX-License-Identifier: Apache-2.0
name: security
on:
pull_request:
push:
branches: [main]
schedule:
- cron: '23 6 * * 1'
workflow_dispatch:
workflow_call:
permissions: {}
concurrency:
group: security-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
security:
uses: KofTwentyTwo/standards/.github/workflows/security.yml@fdf5ed029603c938615a32b1beca3628b8787945 # v0.1.2
permissions:
contents: read
security-events: write
+8 -1
View File
@@ -1,6 +1,13 @@
# Changelog
## 1.1.0 — unreleased
## 1.1.1-rc.1
Release-pipeline candidate: no schema, fixture or wire change from 1.1.0. The
first release built and published by Gitea CI from the tag (bundle, `SHA256SUMS`
and these notes), replacing the hand-published Gitea release and the retired
GitHub release with attestation.
## 1.1.0
Additive contract for printing semantic extension **2.4.0**. Wire payload stays
**v1** and the subprotocol stays `qqq-printing.v1`; every 2.3.0 fixture is unchanged.
+3 -3
View File
@@ -8,7 +8,7 @@ Run both the Java and .NET consumers' protocol tests for a contract change. Chec
Keep wire-v1 payload names stable for native `/qqq/v1/qbit/printing` and explicit legacy `/printing/v1` routes. Optional response fields may be added; clients ignore unknown response fields. Removing/renaming a field, changing its type or changing an existing field's meaning requires explicit versioned compatibility design. Package, printing extension, wire and WebSocket subprotocol versions are independent; see [the version registry](docs/VERSIONS.md). A package version is not permission to silently break an already deployed connector.
`npm run bundle` produces the release archive and checksum in `dist/`. Do not hand-edit generated checksums. Release tags must match the version in package.json, prefixed with `v`. Maintainers publish reviewed artifacts on [Gitea releases](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases). GitHub Actions for this repository were retired on 2026-11-04; retained `.github/scripts` remain local/shared-CI tooling. See [current release provenance](docs/INTEGRATION.md#stable-printing-release) before updating consumer fixture pins.
`npm run bundle` produces the release archive and checksum in `dist/`. Do not hand-edit generated checksums. Release tags must match the version in package.json, prefixed with `v`. A `v<version>` tag on the reviewed merge commit makes Gitea CI rerun every check, build the bundle and create the [Gitea release](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases) with the archive, a fresh `SHA256SUMS` (the bundle's own is checked first) and the matching CHANGELOG section as notes (a `-rc.N` version is a prerelease). Gitea has no equivalent of the retired GitHub artifact attestation: verify downloads against `SHA256SUMS` and the tag. GitHub Actions for this repository were retired on 2026-11-04; retained `.github/scripts` remain local/shared-CI tooling. See [current release provenance](docs/INTEGRATION.md#stable-printing-release) before updating consumer fixture pins.
## Script quality
@@ -21,6 +21,6 @@ npm run typecheck
npm run lint
```
`npm run quality` runs the repository's Python sequence in one step (`uv sync --locked`, both script-quality commands and both source-header commands); the shared Node CI calls it as its `quality-script`. It needs `uv` on `PATH` and `npm ci` first.
`npm run quality` runs the repository's Python sequence in one step (`uv sync --locked`, both script-quality commands and both source-header commands); Gitea CI (`.gitea/workflows/build.yaml`, the shared ci-workflows `node.yaml`) runs the same commands individually, then typecheck, lint, check and bundle. It needs `uv` on `PATH` and `npm ci` first.
`check-script-quality.py` fails for any tracked Python outside `.github/scripts/` or JavaScript outside the `tsconfig.json` include list, and requires Pyright's analyzed-file count to equal the selected scripts. Pyright must run as `pyright -p .github`: it silently skips dot directories from a root config. Documented exceptions: CLI `print`/`console.log` only in `check-*.py` and `tools/`; synthetic test assertions and boundary constants; narrow inline `noqa` comments for fixed no-shell Git/interpreter calls and ordered fail-closed validators. Do not loosen a rule or add a type waiver to pass. `package.json` overrides `minimatch` to 11.0.1, the patched release for [GHSA-g3hv-75wg-m249](https://github.com/advisories/GHSA-g3hv-75wg-m249) (affected before 11.0.1); keep it until ESLint's own dependency range includes a patched release. The only npm license exception, [EX-0015](https://github.com/KofTwentyTwo/standards/blob/main/exceptions/register.md#ex-0015) for development-only `uri-js` 4.4.1, is enforced by `check-script-quality.py` (version, lock integrity, installed license bytes and the exact `pkg:npm/uri-js` caller entry); changing that package or the entry needs renewed approval.
`check-script-quality.py` fails for any tracked Python outside `.github/scripts/` or JavaScript outside the `tsconfig.json` include list, and requires Pyright's analyzed-file count to equal the selected scripts. Pyright must run as `pyright -p .github`: it silently skips dot directories from a root config. Documented exceptions: CLI `print`/`console.log` only in `check-*.py` and `tools/`; synthetic test assertions and boundary constants; narrow inline `noqa` comments for fixed no-shell Git/interpreter calls and ordered fail-closed validators. Do not loosen a rule or add a type waiver to pass. `package.json` overrides `minimatch` to 11.0.1, the patched release for [GHSA-g3hv-75wg-m249](https://github.com/advisories/GHSA-g3hv-75wg-m249) (affected before 11.0.1); keep it until ESLint's own dependency range includes a patched release. The only npm license exception, [EX-0015](https://github.com/KofTwentyTwo/standards/blob/main/exceptions/register.md#ex-0015) for development-only `uri-js` 4.4.1, is enforced by `check-script-quality.py` (version, lock integrity and installed license bytes); changing that package needs renewed approval.
+2 -2
View File
@@ -2,7 +2,7 @@
Shared contract for the [QQQ 4.1+ printing QBit](https://git.kof22.com/QRun-IO/qbit-printing) and [Windows AppKit connector](https://git.kof22.com/QRun-IO/qqq-print-agent). The [realtime transport](docs/REALTIME.md) uses a client-initiated authenticated two-way WebSocket for controls, receipts and job notifications, with separate streamed HTTPS documents and no polling fallback. No inbound connection to the facility is required. This contract is authoritative for wire names; language types may use their idiomatic naming.
Published distribution: [**1.0.0**](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases/tag/v1.0.0). Wire protocol: **v1**. The stable cohort pairs [Windows 1.0.1](https://git.kof22.com/QRun-IO/qqq-print-agent/releases/tag/v1.0.1) with [QBit 1.0.0](https://git.kof22.com/QRun-IO/qbit-printing/releases/tag/v1.0.0), printing semantic API **2.3.0**, and QQQ **4.1.0-RC.21**. See [release and fixture provenance](docs/INTEGRATION.md#stable-printing-release). This repository supplies specification, JSON Schema and synthetic compatibility fixtures; install the Windows connector and integrate the QBit from their own repositories to print documents.
Published distribution: [**1.1.0**](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases/tag/v1.1.0). Wire protocol: **v1**. The stable cohort pairs [Windows 1.2.1](https://git.kof22.com/QRun-IO/qqq-print-agent/releases/tag/v1.2.1) with [QBit 1.1.0](https://git.kof22.com/QRun-IO/qbit-printing/releases/tag/v1.1.0), printing semantic API **2.4.0**, and QQQ **4.1.0-RC.21**; the [1.0.0 cohort](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases/tag/v1.0.0) (Windows 1.0.1, QBit 1.0.0, API 2.3.0) stays compatible. See [release and fixture provenance](docs/INTEGRATION.md#stable-printing-release). This repository supplies specification, JSON Schema and synthetic compatibility fixtures; install the Windows connector and integrate the QBit from their own repositories to print documents.
The stable release includes the [printer media and operator
contract](docs/PRINTER-MEDIA.md) for printing semantic extension **2.3.0** (see the
@@ -13,7 +13,7 @@ granting device management. Use the stable cohort above for these profiles;
older 0.3.2 clients do not support them. Wire version 1 and the client-initiated
realtime transport stay the same.
Source version **1.1.0** (unreleased) adds printing semantic extension **2.4.0**:
Distribution **1.1.0** adds printing semantic extension **2.4.0**:
the `printer-approval-v1` HELLO capability and the server → client `PRINTERS`
frame, which reports each exposed printer's server approval over the
[realtime channel](docs/REALTIME.md#printer-approval). Clients advertise it only
+4 -2
View File
@@ -2,6 +2,8 @@
## Stable printing release
The current primary release is [protocol 1.1.0](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases/tag/v1.1.0) (annotated tag `v1.1.0` at source `5005f3c9e27053b6c327bb923695012832c0fdcc`, bundle `qqq-print-protocol-1.1.0.tar.gz` SHA-256 `541f0f3054cbfe9ce16e7dea58fad01b55a7f0e9246ade8f30df1bb826940703`), adding printing extension 2.4.0's `printer-approval-v1` capability and `PRINTERS` frame. It pairs with [QBit 1.1.0](https://git.kof22.com/QRun-IO/qbit-printing/releases/tag/v1.1.0) and the Windows connector 1.2.1; the 1.0.0 cohort below stays valid for 2.3 peers.
The primary [protocol 1.0.0 release](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases/tag/v1.0.0)
is published on Gitea (release record 1344). This is the specification and fixture
bundle, not an application installer.
@@ -64,7 +66,7 @@ with native printing API 2.3.0 and QQQ 4.1.0-RC.21 as its dependency baseline.
For local development, clone them as siblings. The Java HTTP test defaults to `../qqq-print-protocol/fixtures` and accepts `-Dprinting.protocol.fixtures=<directory>`. The Windows tests link shared fixtures from the sibling repository. See each consumer README for its build and platform requirements.
CI must check out this repository at an immutable commit reviewed with the consumer change. A checkout path of `qqq-print-protocol` beside `qbit-printing` or `qqq-print-agent` preserves the local layout. Do not pin a moving branch for release qualification. The current consumer fixture pin `42c0d249f81395f00408ec0f92638dcbbf5deb03` is distinct from protocol 1.0.0 release source `d652fba1317aec7f25c5a464bac15e97d6094e67`. Maintainers update the pin through a reviewed consumer change; the readable GitHub shadow retains it for the Windows fixture bridge.
CI must check out this repository at an immutable commit reviewed with the consumer change. A checkout path of `qqq-print-protocol` beside `qbit-printing` or `qqq-print-agent` preserves the local layout. Do not pin a moving branch for release qualification. The current consumer fixture pins are `ac835f8b9396b641b525669249fdb4a1e2a50c99` (Windows, the PR 11 commit) and `5005f3c9e27053b6c327bb923695012832c0fdcc` (QBit, the 1.1.0 source); both carry the 1.1.0 fixtures. Maintainers update the pin through a reviewed consumer change; the readable GitHub shadow retains it for the Windows fixture bridge.
If a consumer builds outside a multi-checkout workspace, download the release archive, verify `SHA256SUMS`, extract it and point its tests at `fixtures/`. Consume `schemas/protocol.schema.json#/$defs/<Type>` with a JSON Schema 2020-12 validator.
@@ -100,4 +102,4 @@ npm run bundle
The check runs full draft-2020-12 schema compilation and fixture validation with Ajv, plus independent content/hash/base64 and invalid-input checks. `node tools/verify-fixtures.mjs` remains available without installing dependencies for a quick subset check.
The bundle includes specification, schema, fixtures, tools, dependency manifests and lockfile, and license metadata, excluding installed dependencies. Consumers do not need Node.js to use the schema or fixture JSON. Maintainers publish the bundle and checksum on [Gitea releases](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases); no npm registry publication is performed. Protocol GitHub Actions were retired on 2026-11-04; retained files and historical results do not imply a new hosted build or attestation.
The bundle includes specification, schema, fixtures, tools, dependency manifests and lockfile, and license metadata, excluding installed dependencies. Consumers do not need Node.js to use the schema or fixture JSON. Gitea CI publishes the bundle and checksum on [Gitea releases](https://git.kof22.com/QRun-IO/qqq-print-protocol/releases) from a `v<version>` tag; no npm registry publication is performed and there is no artifact attestation (Gitea has no equivalent). Protocol GitHub Actions were retired on 2026-11-04; retained files and historical results do not imply a new hosted build or attestation.
+2 -2
View File
@@ -46,6 +46,6 @@ additive and keeps the earlier behavior.
QBit 1.0.0 advertises native printing extension 2.3.0. Windows 1.0.1 is the paired
stable client; their product versions do not replace capability discovery.
QBit 1.1.0 advertises 2.4.0 and pairs with Windows 1.2.0; a 2.4.0 server still
serves 1.0.1/1.1.x clients as four-capability peers, and a 1.2.0 client facing
QBit 1.1.0 advertises 2.4.0 and pairs with Windows 1.2.1; a 2.4.0 server still
serves 1.0.1/1.1.x clients as four-capability peers, and a 1.2.1 client facing
2.3.0 does not advertise `printer-approval-v1`.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@qrun-io/qqq-print-protocol",
"version": "1.1.0",
"version": "1.1.1-rc.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@qrun-io/qqq-print-protocol",
"version": "1.1.0",
"version": "1.1.1-rc.1",
"license": "Apache-2.0",
"devDependencies": {
"@eslint/js": "10.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@qrun-io/qqq-print-protocol",
"version": "1.1.0",
"version": "1.1.1-rc.1",
"private": true,
"description": "Versioned wire contract and compatibility fixtures for QQQ cloud printing.",
"license": "Apache-2.0",