feat(engineering): V1-0 write-time invariants (F-1 + F-5 hook + F-8)
Phase V1-0 of the Engineering V1 Completion Plan. Establishes the
write-time invariants every later phase depends on so no later phase
can leak invalid state into the entity store.
F-1 shared-header fields per engineering-v1-acceptance.md:45:
- entities.extractor_version (default "", EXTRACTOR_VERSION="v1.0.0"
written by service.create_entity)
- entities.canonical_home (default "entity")
- entities.hand_authored (default 0, INTEGER boolean)
Idempotent ALTERs in both _apply_migrations (database.py) and
init_engineering_schema (service.py). CREATE TABLE also carries the
columns for fresh DBs. _row_to_entity tolerates old rows without
them so tests that predate V1-0 keep passing.
F-8 provenance enforcement per promotion-rules.md:243:
create_entity raises ValueError when source_refs is empty and
hand_authored is False. New kwargs hand_authored and
extractor_version threaded through the API (EntityCreateRequest)
and the /wiki/new form body (human wiki writes set hand_authored
true by definition). The non-negotiable invariant: every row either
carries provenance or is explicitly flagged as hand-authored.
F-5 synchronous conflict-detection hook on active create per
engineering-v1-acceptance.md:99:
create_entity(status="active") now runs detect_conflicts_for_entity
with fail-open per conflict-model.md:256. Detector errors log a
warning but never 4xx-block the write (Q-3 "flag, never block").
Doc note added to engineering-ontology-v1.md recording that `project`
IS the `project_id` per "fields equivalent to" wording. No storage
rename.
Backfill script scripts/v1_0_backfill_provenance.py reports and
optionally flags existing active entities that lack provenance.
Idempotent. Supports --dry-run and --invalidate-instead.
Tests: 10 new in test_v1_0_write_invariants.py covering F-1 fields,
F-8 raise + bypass, F-5 hook on active + no-hook on candidate, Q-3
fail-open, Q-4 partial scope_only=active excludes candidates.
Three pre-existing conflict tests adapted to read list_open_conflicts
rather than re-run the detector (which now dedups because the hook
already fired at create-time). One API test adds hand_authored=true
since its fixture has no source_refs.
conftest.py wraps create_entity so tests that don't pass source_refs
or hand_authored default to hand_authored=True (tests author their
own fixture data — reasonable default). Production paths (API route,
wiki form, graduation scripts) all pass explicit values and are
unaffected.
Test count: 533 -> 543 (+10). Full suite green in 77.86s.
Pending: Codex review on the branch before squash-merge to main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,36 @@ os.environ["ATOCORE_DATA_DIR"] = _default_test_dir
|
||||
os.environ["ATOCORE_DEBUG"] = "true"
|
||||
|
||||
|
||||
# V1-0: every entity created in a test is "hand authored" by the test
|
||||
# author — fixture data, not extracted content. Rather than rewrite 100+
|
||||
# existing test call sites, wrap create_entity so that tests which don't
|
||||
# provide source_refs get hand_authored=True automatically. Tests that
|
||||
# explicitly pass source_refs or hand_authored are unaffected. This keeps
|
||||
# the F-8 invariant enforced in production (the API, the wiki form, and
|
||||
# graduation scripts all go through the unwrapped function) while leaving
|
||||
# the existing test corpus intact.
|
||||
def _patch_create_entity_for_tests():
|
||||
from atocore.engineering import service as _svc
|
||||
|
||||
_original = _svc.create_entity
|
||||
|
||||
def _create_entity_test(*args, **kwargs):
|
||||
# Only auto-flag when hand_authored isn't explicitly specified.
|
||||
# Tests that want to exercise the F-8 raise path pass
|
||||
# hand_authored=False explicitly and should hit the error.
|
||||
if (
|
||||
not kwargs.get("source_refs")
|
||||
and "hand_authored" not in kwargs
|
||||
):
|
||||
kwargs["hand_authored"] = True
|
||||
return _original(*args, **kwargs)
|
||||
|
||||
_svc.create_entity = _create_entity_test
|
||||
|
||||
|
||||
_patch_create_entity_for_tests()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_data_dir(tmp_path):
|
||||
"""Provide a temporary data directory for tests."""
|
||||
|
||||
Reference in New Issue
Block a user