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
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,19 @@ env:
# lane runs against this revision. The candidate suite never appears here; it
# enters only through .github/ci/candidate-suite.json or the workflow_dispatch
# inputs above, and never replaces this pin.
RELEASED_SUITE_PIN: 0c81c1f8d5321d822be2a2817b05aea03e656e15
#
# 0ed5c691 is the v1.0.0-rc.9 release commit on relux-works/curator-spec
# main, and it publishes the suite this manager qualified against on all
# three runners. Advancing it is fail-closed by construction:
# tests/test_protocol_conformance.py and tests/test_build_metadata.py
# authenticate the released manifest digest before reading a single suite
# byte, and the Windows shard verifier pins this revision too, so a
# half-advanced pin goes red at collection rather than silently
# re-baselining. The digest itself is deliberately NOT repeated here:
# test_no_rc6_literal_survives_outside_the_declaration forbids the candidate
# declaration's identity from appearing in this file, and since rc.9 publishes
# the qualified candidate's bytes the two digests are now the same string.
RELEASED_SUITE_PIN: 0ed5c691e9208eea52f21db2fc05e226ce3516fd

jobs:
typecheck:
Expand Down
2,287 changes: 1,240 additions & 1,047 deletions .research/TASK-260803-2ol7ok_protocol-isolation-classification.json

Large diffs are not rendered by default.

22 changes: 19 additions & 3 deletions .research/TASK-260803-2ol7ok_protocol-shards.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"schema_version": 1,
"task_id": "TASK-260803-2ol7ok",
"source_commit": "2bfe3d64e9142d62e8ea3f92558eeee331f4578a",
"protocol_commit": "0c81c1f8d5321d822be2a2817b05aea03e656e15",
"baseline_count": 1045,
"protocol_commit": "0ed5c691e9208eea52f21db2fc05e226ce3516fd",
"baseline_count": 1053,
"baseline_nodes": [
"tests/test_protocol_conformance.py::test_rc6_candidate_manifest_and_release_record_are_exact_non_release_evidence",
"tests/test_protocol_conformance.py::test_rc6_in_scope_vector_inventory_is_exhaustive",
Expand Down Expand Up @@ -32,6 +32,10 @@
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v7-command-repository.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v7-command-target.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v7-command-driver.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-top-level-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-top-level-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-command-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-command-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[build-receipt-v1.schema.json:build-receipt-v1/valid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[build-receipt-v1.schema.json:build-receipt-v1/invalid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[build-receipt-v1.schema.json:build-receipt-v1/invalid-missing-input.json]",
Expand Down Expand Up @@ -96,6 +100,10 @@
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v7-command-repository.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v7-command-target.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v7-command-driver.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-top-level-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-top-level-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-command-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-command-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[install-marker-v2.schema.json:install-marker-v2/valid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[install-marker-v2.schema.json:install-marker-v2/invalid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[install-marker-v2.schema.json:install-marker-v2/valid-empty-builds.json]",
Expand Down Expand Up @@ -1152,7 +1160,7 @@
"tests/test_protocol_conformance.py::test_registry_client_pagination_rejection_vectors",
"tests/test_protocol_conformance.py::test_registry_client_rollback_state_vectors"
],
"node_count": 565,
"node_count": 573,
"nodes": [
"tests/test_protocol_conformance.py::test_rc6_candidate_manifest_and_release_record_are_exact_non_release_evidence",
"tests/test_protocol_conformance.py::test_rc6_in_scope_vector_inventory_is_exhaustive",
Expand Down Expand Up @@ -1181,6 +1189,10 @@
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v7-command-repository.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v7-command-target.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v7-command-driver.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-top-level-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-top-level-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-command-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[agent-skill-v6.schema.json:agent-skill-v6/invalid-v8-command-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[build-receipt-v1.schema.json:build-receipt-v1/valid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[build-receipt-v1.schema.json:build-receipt-v1/invalid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[build-receipt-v1.schema.json:build-receipt-v1/invalid-missing-input.json]",
Expand Down Expand Up @@ -1245,6 +1257,10 @@
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v7-command-repository.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v7-command-target.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v7-command-driver.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-top-level-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-top-level-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-command-execution-policy.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[csk-skill-v6.schema.json:csk-skill-v6/invalid-v8-command-interpreter.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[install-marker-v2.schema.json:install-marker-v2/valid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[install-marker-v2.schema.json:install-marker-v2/invalid.json]",
"tests/test_protocol_conformance.py::test_rc6_generated_schema_case_is_consumed[install-marker-v2.schema.json:install-marker-v2/valid-empty-builds.json]",
Expand Down
13 changes: 10 additions & 3 deletions .research/TASK-260803-2ol7ok_verify-protocol-shards.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@

TASK_ID = "TASK-260803-2ol7ok"
EXPECTED_SOURCE = "2bfe3d64e9142d62e8ea3f92558eeee331f4578a"
EXPECTED_PROTOCOL = "0c81c1f8d5321d822be2a2817b05aea03e656e15"
EXPECTED_PROTOCOL = "0ed5c691e9208eea52f21db2fc05e226ce3516fd"
# The released suite at EXPECTED_PROTOCOL decides this number: rc.9 publishes
# eight schema cases that rc.6 did not, each of which parametrises one more
# test_rc6_generated_schema_case_is_consumed node. 1045 was the rc.6 figure.
EXPECTED_BASELINE_COUNT = 1053
EXPECTED_TIMEOUT_MINUTES = {
"p00-contract-and-registry": 5,
"p01-lifecycle-cached-baseline": 30,
Expand Down Expand Up @@ -150,8 +154,11 @@ def verify(
baseline = exact_nodes(manifest.get("baseline_nodes"), "manifest.baseline_nodes")
if manifest.get("baseline_count") != len(baseline):
fail("manifest.baseline_count does not equal baseline_nodes length")
if len(baseline) != 1045:
fail(f"manifest baseline must contain 1045 nodes, found {len(baseline)}")
if len(baseline) != EXPECTED_BASELINE_COUNT:
fail(
f"manifest baseline must contain {EXPECTED_BASELINE_COUNT} nodes, "
f"found {len(baseline)}"
)
if collected_path is not None and collected_nodes(collected_path) != baseline:
fail("fresh collection differs from the pinned ordered baseline")

Expand Down
24 changes: 22 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
потребителя по имени; кандидатская линия требует полностью обслуживающего
корня, поэтому непрочитанное семейство больше не может выглядеть зелёным.
- Объявленный кандидат обновлён до `curator-spec` 6001dc3
(`sha256:803918bf...b44403`, протокол `1.0.0-rc.9`). Запиненный released-корень
не изменился.
(`sha256:803918bf...b44403`, протокол `1.0.0-rc.9`).

### Изменено

- `RELEASED_SUITE_PIN` переведён с 0c81c1f8 (протокол `1.0.0-rc.6`) на 0ed5c691 —
релизный коммит `v1.0.0-rc.9` в `curator-spec` main, публикующий ту самую
сюиту, против которой менеджер уже квалифицирован на всех трёх раннерах.
Перевод fail-closed на всех уровнях сразу, иначе прогон краснеет на сборе:
`tests/test_protocol_conformance.py` и `tests/test_build_metadata.py`
аутентифицируют дайджест манифеста до чтения любого байта сюиты, а верификатор
Windows-шардов пинит ревизию отдельно. rc.9 — строгое надмножество rc.6
(ни один файл не удалён, 6 изменены, 243 добавлены), поэтому единственный
эффект на сбор — 8 новых параметризаций
`test_rc6_generated_schema_case_is_consumed` от восьми кейсов `invalid-v8-*`,
которые rc.9 добавляет в `agent-skill-v6` и `csk-skill-v6`: baseline
Windows-шардов вырос с 1045 до 1053 узлов, новые узлы наследуют footprint,
атомарный кластер и шард своей же функции. Там же обновлены пинованные счётчики
исчерпывающей инвентаризации: `agent-skill-v6` и `csk-skill-v6` по 24 -> 28
кейсов, `SCHEMA_CASES` 102 -> 110; остальные счётчики не изменились. Имена
тестов сохраняют исторический префикс `rc6_`: они и есть контракт упорядоченных
node id, переименование переписало бы весь классификатор без выигрыша в
поведении.

### Исправлено

Expand Down
11 changes: 6 additions & 5 deletions tests/test_build_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@
from csk.builds.toolchain import NativeTarget


# The 1.0.0-rc.6 candidate suite that publishes expected/marker-v2.json, the
# marker-v2 writer golden this manager's own marker output is compared against.
# The rc.5 suite (sha256:b6f56aac...) carries no writer golden, so no single root
# can satisfy both this digest and the conformance consumer.
# The released suite at RELEASED_SUITE_PIN -- 1.0.0-rc.9 since the schema-8
# landing -- publishes expected/marker-v2.json, the marker-v2 writer golden this
# manager's own marker output is compared against. The rc.5 suite
# (sha256:b6f56aac...) carries no writer golden, so no single root can satisfy
# both this digest and the conformance consumer.
EXPECTED_MANIFEST_SHA256 = (
"sha256:12e58b82579645ba1ccafba49d3e2dd3216005ddf37ae63c68a9fafd46773071"
"sha256:803918bf8672f76cf990985e51db213b826674cd5bb54fbf47731b8404b44403"
)
NON_UTF8_ENCODINGS = (
"utf-16",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_ci_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def test_candidate_authentication_is_identical_in_fast_and_merge_go_jobs() -> No
def test_the_released_suite_pin_is_declared_once_and_never_inlined() -> None:
workflow = _workflow()
pin = re.findall(r"^ RELEASED_SUITE_PIN: ([0-9a-f]{40})$", workflow, re.MULTILINE)
assert pin == ["0c81c1f8d5321d822be2a2817b05aea03e656e15"]
assert pin == ["0ed5c691e9208eea52f21db2fc05e226ce3516fd"]
assert workflow.count("ref: ${{ env.RELEASED_SUITE_PIN }}") == 4

for job_id in ("fast_ordinary", "fast_protocol", "merge_protocol"):
Expand Down
40 changes: 28 additions & 12 deletions tests/test_protocol_conformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,16 @@

# rc.5 carries the separately versioned external-repository corpus consumed by
# test_rc5_external_repository_conformance.py. This module intentionally binds
# the later rc.6 manager/build corpus; do not make an rc.5 root fail collection
# merely because both authenticated consumers share the conventional root env.
# the released manager/build corpus at RELEASED_SUITE_PIN -- 1.0.0-rc.9 since
# the schema-8 landing -- so do not make an rc.5 root fail collection merely
# because both authenticated consumers share the conventional root env.
#
# The test names keep their historical rc6_ prefix on purpose. The Windows
# protocol lane pins an ORDERED node-id baseline in
# .research/TASK-260803-2ol7ok_protocol-shards.json together with a per-node
# isolation classification; renaming the functions would rewrite every one of
# those rows for no behavioural gain and is a separate change. What the module
# authenticates is the identity below, not the prefix.
if ROOT_TEXT:
_candidate_manifest = Path(ROOT_TEXT) / "manifest.json"
if _candidate_manifest.is_file() and hashlib.sha256(
Expand All @@ -86,9 +94,9 @@
)

EXPECTED_CANDIDATE_MANIFEST_SHA256 = (
"sha256:12e58b82579645ba1ccafba49d3e2dd3216005ddf37ae63c68a9fafd46773071"
"sha256:803918bf8672f76cf990985e51db213b826674cd5bb54fbf47731b8404b44403"
)
EXPECTED_CANDIDATE_PROTOCOL_VERSION = "1.0.0-rc.6"
EXPECTED_CANDIDATE_PROTOCOL_VERSION = "1.0.0-rc.9"
EXPECTED_BUILD_DRIVER_FILES = (
"build-input.ccj.json",
"build-source-sha256.txt",
Expand Down Expand Up @@ -200,7 +208,7 @@ def _golden_bytes(relative: str) -> bytes:
def _repository_root() -> Path:
repository = _root().parent.parent
assert (repository / "schemas" / "v1").is_dir()
assert (repository / "release" / "1.0.0-rc.6.json").is_file()
assert (repository / "release" / "1.0.0-rc.9.json").is_file()
return repository


Expand Down Expand Up @@ -390,7 +398,7 @@ def test_rc6_candidate_manifest_and_release_record_are_exact_non_release_evidenc
manifest_digest = "sha256:" + hashlib.sha256(manifest_raw).hexdigest()
manifest = json.loads(manifest_raw)
release = json.loads(
(_repository_root() / "release" / "1.0.0-rc.6.json").read_text(
(_repository_root() / "release" / "1.0.0-rc.9.json").read_text(
encoding="utf-8"
)
)
Expand All @@ -408,25 +416,31 @@ def test_rc6_candidate_manifest_and_release_record_are_exact_non_release_evidenc
)
assert release["downstream_consumption"]["environment"] == "CURATOR_CONFORMANCE_ROOT"
assert release["downstream_consumption"]["committed_release_pin_advanced"] is False
assert release["claim_v3"]["claims_emitted"] == []
assert release["claim_v3"]["rc6_claim_schema"] is None
assert release["claim_v5"]["claims_emitted"] == []
assert release["claim_v5"]["claim_protocol_version"] == (
EXPECTED_CANDIDATE_PROTOCOL_VERSION
)


def test_rc6_in_scope_vector_inventory_is_exhaustive() -> None:
schema_counts = {
schema: sum(entry["schema"] == schema for entry in SCHEMA_CASES)
for schema in IN_SCOPE_SCHEMA_NAMES
}
# rc.9 adds four invalid-v8-* cases to each of the two v6 manifest families
# -- a v6 schema must keep rejecting the schema-8 execution_policy and
# interpreter fields, top level and per command. Every other in-scope family
# is byte-for-byte the rc.6 inventory.
assert schema_counts == {
"agent-skill-v6.schema.json": 24,
"agent-skill-v6.schema.json": 28,
"build-receipt-v1.schema.json": 18,
"conformance-claim-v1.schema.json": 2,
"conformance-claim-v2.schema.json": 7,
"conformance-claim-v3.schema.json": 13,
"csk-skill-v6.schema.json": 24,
"csk-skill-v6.schema.json": 28,
"install-marker-v2.schema.json": 14,
}
assert len(SCHEMA_CASES) == 102
assert len(SCHEMA_CASES) == 110
assert len(BUILD_DRIVER_VECTORS["positive_cases"]) == 8
assert len(BUILD_DRIVER_VECTORS["rejection_cases"]) == 77
assert set(_BUILD_REJECTION_BINDINGS) == {
Expand Down Expand Up @@ -974,7 +988,9 @@ def test_rc6_claim_v3_schema_stays_on_rc5_and_requires_build_drivers() -> None:
ids=lambda rule: rule["name"],
)
def test_rc6_claim_qualification_rule(rule: dict[str, Any]) -> None:
assert CLAIM_QUALIFICATION_VECTORS["protocol_version"] == "1.0.0-rc.6"
assert CLAIM_QUALIFICATION_VECTORS["protocol_version"] == (
EXPECTED_CANDIDATE_PROTOCOL_VERSION
)
assert CLAIM_QUALIFICATION_VECTORS["claim_schema_version"] == 3
assert CLAIM_QUALIFICATION_VECTORS["candidate_claims_emitted"] == []
if rule["name"] == "schema-valid-is-not-qualified":
Expand Down
6 changes: 3 additions & 3 deletions tests/test_protocol_shards.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@ def test_accepted_manifest_is_exhaustive_disjoint_and_bounded() -> None:
assert isinstance(shards, list)
flattened = [node for shard in shards for node in shard["nodes"]]

assert len(baseline) == 1045
assert len(baseline) == 1053
assert set(flattened) == set(baseline)
assert len(flattened) == len(set(flattened))
assert {shard["id"]: shard["timeout_minutes"] for shard in shards} == EXPECTED_TIMEOUTS
assert {shard["id"]: shard["node_count"] for shard in shards} == {
"p00-contract-and-registry": 565,
"p00-contract-and-registry": 573,
"p01-lifecycle-cached-baseline": 444,
"p02-lifecycle-sabotage-a": 9,
"p03-lifecycle-sabotage-b": 9,
Expand All @@ -114,7 +114,7 @@ def test_verifier_accepts_the_exact_inventory(accepted_inputs: tuple[ModuleType,
evidence = _verified_without_git(module, manifest_path, collected)

assert evidence["ok"] is True
assert evidence["baseline_nodes"] == 1045
assert evidence["baseline_nodes"] == 1053
assert evidence["overlap"] == 0
assert evidence["gap"] == 0

Expand Down
Loading