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: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "omnimarket"
version = "0.4.199"
version = "0.4.200"
description = "OmniMarket - Portable ONEX workflow package registry"
authors = [
{name = "OmniNode.ai", email = "contact@omninode.ai"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
is_seam_ticket: false
interface_change: false
interfaces_touched: []
evidence_requirements:
{requirements_block}evidence_requirements:
- kind: "ci"
description: "PR #{pr_number} product diff scope present"
command: "gh pr view ${{PR_NUMBER}} --repo ${{REPO}} --json files"
Expand Down Expand Up @@ -915,6 +915,94 @@ def render_ac_bindings_block(
return " binds_ac:\n" + labels + " ac_bindings:\n" + "".join(entries)


#: The id of the single requirement the transcriber emits. One requirement
#: holding every transcribed criterion, not one per criterion: a requirement is
#: a statement of what is wanted and the criteria are how it is settled, and
#: the transcriber has exactly one thing to say -- "these are the criteria this
#: ticket's author declared". Splitting them would invent a requirement
#: boundary nobody wrote.
TRANSCRIBED_REQUIREMENT_ID = "req-transcribed-acceptance-criteria"

#: ``ModelRequirement.statement`` is required and rejects whitespace. This says
#: what the block IS, including the direction of authority, so a reader of the
#: contract is not left to infer that these words came from the tracker.
TRANSCRIBED_REQUIREMENT_STATEMENT = (
"The acceptance criteria this ticket's author declared, transcribed from "
"the ticket body at autobind time by occ-autobind. The ticket body is the "
"source and this block is the copy; each criterion's id is the label the "
"sibling binds_ac entry claims."
)

#: Indents inside the ``requirements:`` block. ``acceptance`` entries sit two
#: levels below the top-level key, so a criterion's ``statement`` renders at 8 --
#: the same budget ``render_check_value_field`` was measured against for a
#: contract's ``check_value``, which is why its default applies unchanged.
_REQUIREMENT_STATEMENT_INDENT = 4
_CRITERION_STATEMENT_INDENT = 8


def render_requirements_block(
records: Sequence[ModelTranscribedBinding],
) -> str:
"""The top-level ``requirements:`` block for a transcribed companion.

OMN-19038. ``binds_ac`` names the criteria an evidence item claims and
``ac_bindings`` pins each one by hash, but neither carries the criterion's
WORDS. Measured on ``origin/dev`` at ``605ebfd729``: of 9,300 contracts,
370 carry a ``binds_ac`` entry and one carries ``requirements`` -- so the
OMN-18270 serializer, which renders ``requirements[].acceptance[]`` into
the ticket body and treats the contract as the model, had essentially no
input anywhere in the corpus.

The text was never missing. :func:`omnimarket.occ_criterion_units.
criterion_units` produces it as ``unit.text``, the transcriber hashes it,
and until now dropped it. This renders it.

**Why this takes the same record list as** :func:`render_ac_bindings_block`
**and not its own.** The serializer refuses a contract that binds a label
its model does not enumerate (``ac_section_unenumerated_binding``), and the
OMN-18333 rule ``ac_binding_criterion_unbound`` refuses the converse -- a
declared criterion nothing claims. A model built from a second filter would
satisfy neither reliably, and the disagreement would be invisible at mint
time and permanent afterwards. Taking one list makes the declared set and
the bound set equal BY CONSTRUCTION rather than by agreement between two
pieces of code that will drift.

Returns the empty string for no records, so a ticket that declared no
falsifiers mints a contract byte-identical to the one it minted before this
function existed. That is most of them.

Each ``statement`` renders through :func:`render_check_value_field`, which
emits a literal block scalar (``|-``) for any value whose quoted form
yamlfmt would refold. Criterion text is prose and routinely long, so this
is the normal path rather than the exception; a plain scalar there gets the
formatter's sentinel injected into the value and the OMN-15479
contamination ratchet then rejects the file, correctly.
"""
if not records:
return ""
lines = [
"requirements:\n",
f' - id: "{TRANSCRIBED_REQUIREMENT_ID}"\n',
render_check_value_field(
"statement",
TRANSCRIBED_REQUIREMENT_STATEMENT,
indent=_REQUIREMENT_STATEMENT_INDENT,
),
" acceptance:\n",
]
for record in records:
lines.append(f' - id: "{record.label}"\n')
lines.append(
render_check_value_field(
"statement",
record.statement,
indent=_CRITERION_STATEMENT_INDENT,
)
)
return "".join(lines)


def render_behavior_proof_dod_evidence_item(
*,
repo: str,
Expand Down Expand Up @@ -1393,7 +1481,7 @@ def render_compute_contract_summary_field(*, repo: str, pr_number: int) -> str:
{summary_field}is_seam_ticket: false
interface_change: false
interfaces_touched: []
evidence_requirements:
{requirements_block}evidence_requirements:
- kind: "ci"
description: "PR #{pr_number} CI checks green"
command: "gh pr checks ${{PR_NUMBER}} --repo ${{REPO}}"
Expand Down Expand Up @@ -2209,6 +2297,11 @@ def render_companion_contract(
_CONTRACT_HEAD_TEMPLATE.format(
ticket_id=ticket_id,
pr_number=pr_number,
# OMN-19038. The SAME record list that renders binds_ac below, so
# the model the serializer reads and the bindings the gate resolves
# are two renderings of one filtered unit list. Empty renders the
# empty string, which is the pre-OMN-19038 head byte for byte.
requirements_block=render_requirements_block(ac_bindings),
# The ONE derivation, computed once above and consumed by both the
# declaration and the executed item, so the contract's stated
# requirement and its executed check cannot disagree.
Expand Down Expand Up @@ -2606,6 +2699,12 @@ def render_compute_downstream_dod_evidence_item(
summary_field=render_compute_contract_summary_field(
repo=repo, pr_number=pr_number
),
# Empty by construction, not by omission: this renderer keeps only the
# tail from the `dod_evidence:` marker onward, so anything rendered
# into the head's preamble is discarded. A repair row appends ONE
# evidence item to a contract that already exists; it does not
# re-author that contract's model.
requirements_block="",
behavior_evidence_requirement=render_behavior_evidence_requirement(
repo=repo, pr_number=pr_number, changed_files=()
),
Expand Down Expand Up @@ -2704,6 +2803,12 @@ def render_compute_companion_contract(
summary_field=render_compute_contract_summary_field(
repo=repo, pr_number=pr_number
),
# OMN-19038, wired on BOTH producers deliberately. OMN-18332 is the
# precedent: the transcription landed on this compute path, was not
# wired into the born path, and seven of the next nine companions
# minted unbound. A model emitted by one producer and not the other
# would reproduce that exactly, one layer up.
requirements_block=render_requirements_block(ac_bindings),
behavior_evidence_requirement=render_behavior_evidence_requirement(
repo=repo, pr_number=pr_number, changed_files=changed_files
),
Expand Down
20 changes: 20 additions & 0 deletions src/omnimarket/occ_ac_transcription.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ class ModelTranscribedBinding(BaseModel):
"digest matches nothing the gate computes."
),
)
statement: str = Field(
...,
description=(
"The criterion's own words, canonicalised exactly as "
"`canonical_criterion_text` produced them for the hash above. "
"OMN-19038: this text was already in hand at every mint and was "
"discarded, so the corpus held 370 bindings and one copy of the "
"criteria they name. It is REQUIRED and has no default, because a "
"binding whose statement defaulted to empty would render a model "
"declaring a criterion with no content -- which reads as an "
"enumerated criterion to the serializer and is worse than the "
"absence it replaced."
),
min_length=1,
)
falsifier: str = Field(
...,
description="The check the author declared would settle this criterion.",
Expand Down Expand Up @@ -217,6 +232,11 @@ def transcribe_ac_bindings(
ModelTranscribedBinding(
label=unit.label,
criterion_hash=pinned,
# OMN-19038. The text the hash above was taken over, carried
# rather than dropped. It comes from the SAME `unit`, so the
# statement and the digest can never describe different
# sentences.
statement=unit.text,
falsifier=unit.falsifier,
proposed_by=proposed_by,
accepted_by=acceptor if may_accept else None,
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/occ/test_omn_18332_born_path_transcription.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ def _accepted(label: str) -> ModelTranscribedBinding:
return ModelTranscribedBinding(
label=label,
criterion_hash=_hash(f"criterion {label}"),
# OMN-19038 made the criterion's own text a required field, so these
# fixtures now carry it. The bindings this module asserts on are
# unchanged; the statement simply travels beside them.
statement=f"{label}: criterion {label} -- falsifier: pytest -k {label}",
falsifier=f"pytest -k {label}",
proposed_by=AUTOBINDER_IDENTITY,
accepted_by="user-id-of-the-author",
Expand All @@ -60,6 +64,7 @@ def _draft(label: str) -> ModelTranscribedBinding:
return ModelTranscribedBinding(
label=label,
criterion_hash=_hash(f"criterion {label} as edited"),
statement=f"{label}: criterion {label} as edited -- falsifier: pytest -k {label}",
falsifier=f"pytest -k {label}",
proposed_by=AUTOBINDER_IDENTITY,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,11 @@ def test_the_model_refuses_a_half_populated_acceptance(self) -> None:
ModelTranscribedBinding(
label="AC1",
criterion_hash=hashlib.sha256(b"x").hexdigest(),
# Populated so the half-acceptance rule is what fails here.
# Without it (OMN-19038 made the field required) this raised a
# missing-field error instead and the test passed on the wrong
# exception.
statement="AC1: the thing holds -- falsifier: run it",
falsifier="run it",
proposed_by=AUTOBINDER_IDENTITY,
accepted_by=AUTHOR,
Expand Down
Loading
Loading