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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ here.

### Added

- RFC 0020 limits the later-sibling `limit-exceeded` rule to instances admitted for acquisition;
admission failures keep their own codes, with a recorded positive vector and negative twin (#213).
- Document ceiling profiles (#232): default `base` and declared `extended`, a shared
ceiling table, explicit unsupported-profile refusal, capability discovery and
byte identity. Schemas retain base bounds; profile conformance covers negotiation.
Expand Down
4 changes: 4 additions & 0 deletions conformance/workflow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Each summary's `recordsDigest` was fixed independently as SHA-256 of the canonic
its stage's dispositions in recorded order. The repository test reconstructs those exact bytes,
recomputes every summary counter and digest, redraws the sample, and reconstructs the complete
drift result. The vectors are deliberately authored inputs, not output captured from an executor.
The later-sibling pair records an admitted instance, the instance that exceeds the record limit,
a projection failure that keeps `projection-invalid`, and an admitted later sibling that records
`limit-exceeded`; its negative twin changes only the projection failure and expects the verifier's
`inventory-mismatch` finding.

This fixture establishes no implementation, execution, authentication, verdict or conformance
claim. The full research recording described by companion §11 remains future work.
3 changes: 3 additions & 0 deletions conformance/workflow/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
{ "path": "reports/sample-size-over-bound.json", "schemaValid": true },
{ "path": "reports/sample-seed-invalid.json", "schemaValid": true },
{ "path": "reports/summary-mismatch.json", "schemaValid": true },
{ "path": "reports/later-sibling-admission-wrong-failure.json", "schemaValid": true },
{ "path": "vectors/sample-size-over-bound.json", "schemaValid": true },
{ "path": "vectors/later-sibling-admission.json", "schemaValid": true },
{ "path": "vectors/later-sibling-admission-wrong-failure.json", "schemaValid": true },
{ "path": "vectors/sample-seed-invalid.json", "schemaValid": false },
{ "path": "vectors/sample-seed-empty.json", "schemaValid": false },
{ "path": "vectors/sample-seed-missing.json", "schemaValid": false },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"result": "finding",
"finding": "inventory-mismatch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"vectorVersion": 1,
"kind": "later-sibling-admission",
"stage": "people",
"maxRecords": 2,
"instances": [
{
"id": "admitted-first",
"admission": "admitted",
"requestedOutputs": 1,
"record": {
"caseDigest": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"state": "completed", "failure": null, "attempts": 1
}
},
{
"id": "bound-trigger",
"admission": "admitted",
"requestedOutputs": 2,
"record": {
"caseDigest": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
"state": "failed", "failure": "limit-exceeded", "attempts": 1
}
},
{
"id": "projection-failed",
"admission": "projection-invalid",
"requestedOutputs": 0,
"record": {
"caseDigest": null, "state": "failed", "failure": "limit-exceeded", "attempts": 0
}
},
{
"id": "admitted-later",
"admission": "admitted",
"requestedOutputs": 0,
"record": {
"caseDigest": "sha256:4444444444444444444444444444444444444444444444444444444444444444",
"state": "failed", "failure": "limit-exceeded", "attempts": 0
}
}
],
"expectedFinding": "inventory-mismatch"
}
43 changes: 43 additions & 0 deletions conformance/workflow/vectors/later-sibling-admission.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"vectorVersion": 1,
"kind": "later-sibling-admission",
"stage": "people",
"maxRecords": 2,
"instances": [
{
"id": "admitted-first",
"admission": "admitted",
"requestedOutputs": 1,
"record": {
"caseDigest": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"state": "completed", "failure": null, "attempts": 1
}
},
{
"id": "bound-trigger",
"admission": "admitted",
"requestedOutputs": 2,
"record": {
"caseDigest": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
"state": "failed", "failure": "limit-exceeded", "attempts": 1
}
},
{
"id": "projection-failed",
"admission": "projection-invalid",
"requestedOutputs": 0,
"record": {
"caseDigest": null, "state": "failed", "failure": "projection-invalid", "attempts": 0
}
},
{
"id": "admitted-later",
"admission": "admitted",
"requestedOutputs": 0,
"record": {
"caseDigest": "sha256:4444444444444444444444444444444444444444444444444444444444444444",
"state": "failed", "failure": "limit-exceeded", "attempts": 0
}
}
]
}
18 changes: 13 additions & 5 deletions rfcs/0020-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,11 @@ containers and unsuccessful work; nothing is truncated to fit. The same bounds h
bytes and input bytes. A workflow runner refuses admission before acquisition when a known bound
fails. Account for results in instance order: stage order, then selected parent-record order,
then output index, regardless of acquisition or completion order. The first instance whose
outputs would exceed a branch bound and every later instance of that stage record
`limit-exceeded`, emitting no outputs. Earlier completed siblings' outputs remain in the closed
set with those failures explicit. Nothing is truncated to fit.
outputs would exceed a branch bound and every later instance of that stage admitted for acquisition
record `limit-exceeded`, emitting no outputs. Under §§3 and 7, an instance that fails projection or
influence admission is not admitted for acquisition and keeps its own failure code regardless of
its position in the stage. Earlier completed siblings' outputs remain in the closed set with those
failures explicit. Nothing is truncated to fit.

### 3. Stages and projected cases

Expand Down Expand Up @@ -739,7 +741,9 @@ check 3, an earlier instance's `influence-mismatch` precedes a later one's `case
(`case-mismatch`), then inherited classes, influence and explicit admission
(`influence-mismatch`).
4. The expected instance inventory, admission keys, attempts, terminal states, selection refusals,
deterministic bound accounting and stage closure (`inventory-mismatch`); then re-extract all
deterministic bound accounting and stage closure (`inventory-mismatch`), applying §2's
later-sibling rule only after each instance's admission status is established and only to
admitted instances, so an instance refused at admission keeps its own code; then re-extract all
outputs, compare payload bytes, order, complete sets, parent/selection references, relation and
lineage pins (`output-mismatch`). Neither failure nor missing data becomes `[]`.
5. From those checked output records and their retained dispositions, recompute every stage
Expand Down Expand Up @@ -937,7 +941,11 @@ revisions or relations, adding an unselected child, omitting or repeating a sele
laundering generated influence. Include a record-class downstream operation parameterised from a
generated payload without generated admission, refused before acquisition, including an affected
case parameter its request never names. Include two siblings of six outputs when ten records
remain, completed in both orders: the second and every later sibling record `limit-exceeded`.
remain, completed in both orders: the second and every later admitted sibling record
`limit-exceeded`. In one stage record, in order, an admitted completed instance, an admitted
`limit-exceeded` instance, a `projection-invalid` instance and an admitted later sibling that
records `limit-exceeded`; mutate the projection failure to `limit-exceeded` for an
`inventory-mismatch` twin.
Include interruption after atomic admission, uncertain acquisition, a recorded selection bound
refusal, and retries before and after consumption. Freeze expected reports with the recordings.
Add integer and named family-3 variants with matching published identities, and refuse changed
Expand Down
48 changes: 47 additions & 1 deletion schema/workflow-1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
{ "$ref": "#/$defs/drift" },
{ "$ref": "#/$defs/bundleFixture" },
{ "$ref": "#/$defs/fixtureManifest" },
{ "$ref": "#/$defs/refusalVector" }
{ "$ref": "#/$defs/refusalVector" },
{ "$ref": "#/$defs/instanceStateVector" }
],
"$defs": {
"id": {
Expand Down Expand Up @@ -805,6 +806,51 @@
},
"additionalProperties": false
},
"instanceStateVector": {
"type": "object",
"required": ["vectorVersion", "kind", "stage", "maxRecords", "instances"],
"properties": {
"vectorVersion": { "const": 1 },
"kind": { "const": "later-sibling-admission" },
"stage": { "$ref": "#/$defs/id" },
"maxRecords": { "$ref": "#/$defs/positiveCount" },
"instances": {
"type": "array",
"minItems": 4,
"maxItems": 4,
"items": {
"type": "object",
"required": ["id", "admission", "requestedOutputs", "record"],
"properties": {
"id": { "$ref": "#/$defs/id" },
"admission": { "enum": ["admitted", "projection-invalid"] },
"requestedOutputs": { "$ref": "#/$defs/count" },
"record": {
"type": "object",
"required": ["caseDigest", "state", "failure", "attempts"],
"properties": {
"caseDigest": {
"oneOf": [{ "$ref": "#/$defs/digest" }, { "type": "null" }]
},
"state": { "enum": ["completed", "failed"] },
"failure": {
"oneOf": [
{ "type": "null" },
{ "enum": ["projection-invalid", "limit-exceeded"] }
]
},
"attempts": { "$ref": "#/$defs/count" }
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"expectedFinding": { "const": "inventory-mismatch" }
},
"additionalProperties": false
},
"refusalVector": {
"oneOf": [
{
Expand Down
18 changes: 13 additions & 5 deletions spec/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ containers and unsuccessful work; nothing is truncated to fit. The same bounds h
bytes and input bytes. A workflow runner refuses admission before acquisition when a known bound
fails. Account for results in instance order: stage order, then selected parent-record order,
then output index, regardless of acquisition or completion order. The first instance whose
outputs would exceed a branch bound and every later instance of that stage record
`limit-exceeded`, emitting no outputs. Earlier completed siblings' outputs remain in the closed
set with those failures explicit. Nothing is truncated to fit.
outputs would exceed a branch bound and every later instance of that stage admitted for acquisition
record `limit-exceeded`, emitting no outputs. Under §§3 and 7, an instance that fails projection or
influence admission is not admitted for acquisition and keeps its own failure code regardless of
its position in the stage. Earlier completed siblings' outputs remain in the closed set with those
failures explicit. Nothing is truncated to fit.

## 3. Stages and projected cases

Expand Down Expand Up @@ -561,7 +563,9 @@ check 3, an earlier instance's `influence-mismatch` precedes a later one's `case
(`case-mismatch`), then inherited classes, influence and explicit admission
(`influence-mismatch`).
4. The expected instance inventory, admission keys, attempts, terminal states, selection refusals,
deterministic bound accounting and stage closure (`inventory-mismatch`); then re-extract all
deterministic bound accounting and stage closure (`inventory-mismatch`), applying §2's
later-sibling rule only after each instance's admission status is established and only to
admitted instances, so an instance refused at admission keeps its own code; then re-extract all
outputs, compare payload bytes, order, complete sets, parent/selection references, relation and
lineage pins (`output-mismatch`). Neither failure nor missing data becomes `[]`.
5. From those checked output records and their retained dispositions, recompute every stage
Expand Down Expand Up @@ -759,7 +763,11 @@ revisions or relations, adding an unselected child, omitting or repeating a sele
laundering generated influence. Include a record-class downstream operation parameterised from a
generated payload without generated admission, refused before acquisition, including an affected
case parameter its request never names. Include two siblings of six outputs when ten records
remain, completed in both orders: the second and every later sibling record `limit-exceeded`.
remain, completed in both orders: the second and every later admitted sibling record
`limit-exceeded`. In one stage record, in order, an admitted completed instance, an admitted
`limit-exceeded` instance, a `projection-invalid` instance and an admitted later sibling that
records `limit-exceeded`; mutate the projection failure to `limit-exceeded` for an
`inventory-mismatch` twin.
Include interruption after atomic admission, uncertain acquisition, a recorded selection bound
refusal, and retries before and after consumption. Freeze expected reports with the recordings.
Add integer and named family-3 variants with matching published identities, and refuse changed
Expand Down
77 changes: 77 additions & 0 deletions tests/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ def test_two_stage_plan_carries_bulk_and_refusal_vectors(self) -> None:
self.assertIn("For every new refusal code, retain a valid expected-report", unwrapped)
self.assertIn("Interrupted and resumed variants retain", unwrapped)

def test_later_sibling_rule_is_limited_to_admitted_instances(self) -> None:
companion = (ROOT / "spec" / "workflow.md").read_text(encoding="utf-8")
section = companion.split("\n## 2. ", 1)[1].split("\n## 3. ", 1)[0]
unwrapped = section.replace("\n", " ")
self.assertIn("every later instance of that stage admitted for acquisition", unwrapped)
self.assertIn("Under §§3 and 7", unwrapped)
self.assertIn("keeps its own failure code", unwrapped)


class WorkflowFixtureTests(unittest.TestCase):
@classmethod
Expand Down Expand Up @@ -275,6 +283,75 @@ def test_recorded_sample_is_redrawn_from_seed(self) -> None:
sample = json.loads((FIXTURE / "sample.json").read_text(encoding="utf-8"))
self.assertEqual(sample["selected"], redraw_sample(sample))

def test_later_sibling_vector_preserves_admission_failures(self) -> None:
def verify(vector: dict[str, object]) -> dict[str, str]:
remaining = vector["maxRecords"]
assert isinstance(remaining, int)
later_sibling_rule = False
for instance in vector["instances"]:
admission = instance["admission"]
requested = instance["requestedOutputs"]
if admission == "projection-invalid":
expected = {"state": "failed", "failure": "projection-invalid"}
elif later_sibling_rule or requested > remaining:
expected = {"state": "failed", "failure": "limit-exceeded"}
later_sibling_rule = True
else:
expected = {"state": "completed", "failure": None}
remaining -= requested
actual = {
"state": instance["record"]["state"],
"failure": instance["record"]["failure"],
}
if actual != expected:
return {"result": "finding", "finding": "inventory-mismatch"}
return {"result": "reading", "consistency": "agrees"}

accepted = json.loads(
(FIXTURE / "vectors" / "later-sibling-admission.json").read_text(encoding="utf-8")
)
wrong = json.loads(
(FIXTURE / "vectors" / "later-sibling-admission-wrong-failure.json").read_text(
encoding="utf-8"
)
)
self.assertEqual(
[
("admitted", "completed", None),
("admitted", "failed", "limit-exceeded"),
("projection-invalid", "failed", "projection-invalid"),
("admitted", "failed", "limit-exceeded"),
],
[
(row["admission"], row["record"]["state"], row["record"]["failure"])
for row in accepted["instances"]
],
)
self.assertEqual(verify(accepted), {"result": "reading", "consistency": "agrees"})
report = json.loads(
(FIXTURE / "reports" / "later-sibling-admission-wrong-failure.json").read_text(
encoding="utf-8"
)
)
self.assertEqual(wrong["expectedFinding"], "inventory-mismatch")
self.assertEqual(verify(wrong), report)
self.validator.validate(report)
self.assertEqual(
{"caseDigest": None, "attempts": 0},
{
member: accepted["instances"][2]["record"][member]
for member in ("caseDigest", "attempts")
},
)
self.assertEqual(
wrong["instances"][2]["record"]["failure"],
"limit-exceeded",
)
repaired_twin = json.loads(json.dumps(wrong))
repaired_twin.pop("expectedFinding")
repaired_twin["instances"][2]["record"]["failure"] = "projection-invalid"
self.assertEqual(repaired_twin, accepted)

def test_drift_is_recomputed_from_the_two_recorded_bundles(self) -> None:
left = json.loads((FIXTURE / "left-bundle.json").read_text(encoding="utf-8"))
right = json.loads((FIXTURE / "right-bundle.json").read_text(encoding="utf-8"))
Expand Down
Loading