Tyapkin
Capkin

Federation, imports и Flow

Проектная граница Capkin types, contract imports, федеративных полей, CEL и причинного Flow.

Federation, imports и Flow

Кандидат multi-transition Machine

Новый opt-in tyapkin-port-bindings-v2 связывает protobuf descriptor с полным strict kernel Machine, включая несколько Input, Guard, Transition и Output. binding-build сверяет CapIrV1 digest и покрытие всех портов. ContractStore принимает machine-kernel-v1 и flow-v2 в отдельном tyapkin-contract-package-v6-multimachine; graph release v5 требует v5 binding/runtime schema. Локальный fixture port-binding-multi-v1 проверяет двухшаговую Machine, публикацию двух owner-пакетов, Flow и отказы при изменённом Transition, пропущенном Input, чужом digest и старом ABI.

Веточный срез пока не является опубликованным API или принятым Allium release. Локальный Flow переносит committed Output в запрос Input; target Guard, tenant scope и внешний receipt остаются отдельными проверками host.

Наглядная локальная сборка

Для приёмки двух независимых Machine есть локальный runbook docs/runbooks/capkin-federation-demo.md. Команда pnpm demo:capkin:federation записывает отдельное acceptance.webm: React Flow показывает Asset, ServiceCase, их четыре protobuf TypeRef и пакет AssetToCase. Inspector раскрывает точный Capkin .feature, package/import digest и правило переноса asset_id/case_id. Проверка сборки запускает реальный compiler и publication checker; успешный runtime затем показывает два Trace и производную federated read projection. demo.webm сохраняет техническую регрессию 15 сценариев. Схема читает pinned fixture и не редактирует Allium ApplicationRelease.

Вторая вкладка того же React Flow показывает отдельный pinned graph release ServiceCase → CaseToScheduleCelV2 → Scheduling. Человек видит строгий Capkin .feature, выражение CEL v2, исходное case_id: string, целевые case_id: string и reason: string, а после проверки и запуска — reason = maintenance и target State Scheduled. Проверка использует binding checker, CEL golden и Rust graph checker. CEL исполняется в Rust reference host, не в браузере; этот release не входит в три packages первой вкладки.

Над сборкой расположен общий проводник сущностей. Он собирает read-only индекс из строгих source declarations и уже проверенных Machine, Kernel, Type IR, Flow graph и read entity contexts. Сейчас это 339 сущностей 22 видов и 484 связи. В одном поиске доступны Machine, Principal, Capability, State, Input, Guard, Transition, Output, GuardSet, EvidenceComponent, bindings, SemanticPort, TypeRef, Field, Flow, Projection, Package, Release и поля read entity. Выбор узла показывает его прямые связи, точный контекст, статус свидетельства и переход к подробной студии. Одноимённые узлы из разных release не объединяются. Это проекция локального corpus: ExecutionReceipt и Trace появляются только после отдельного запуска, а пунктирные source-symbol совпадения не считаются release binding. Во время локального запуска KernelHost::verified_trace отдаёт именно те Commitment records, которые прошли Rust replay и проверку checkpoint. Node не перечитывает SQLite под меткой verified Trace. Проводник связывает OutputIntent и его Commitment с локальной Flow delivery, затем с точным Input Commitment целевой Machine. Теперь FlowRuntime::verify_local_delivery также повторно проверяет source Output и command целевого Input, pinned predecessor и сохранённый command head. Отдельный targetInputCommitmentHash позволяет показать правильный Input, даже если Machine позже выполнила ещё одну команду. Браузер отклоняет witness, который не совпадает с проверенной Trace; он не выбирает первый Input по имени Machine. Локальный формат tyapkin-verified-local-flow-delivery-v1 не является внешним proof API. Fanout показывает две отдельные delivery и три независимые Trace. Typed effect и composite GuardSet также возвращают проверенные Rust Trace своих Machine, включая отдельные PASS, DENY и INDETERMINATE instances. Runtime facts живут в текущей вкладке; локальная delivery/receipt не подтверждает внешнего эффекта.

Проводник также может прочитать любой явно выбранный локальный ContractStore. Команда Rust capkin contract-inspect --db <registry.sqlite> открывает существующую базу только для чтения, повторно проверяет digest и структуру каждого опубликованного package и выдаёт versioned inspection: imports, exports, source bytes, compiler IR, artifacts и typed effect sources. Если при старте reference server задан DEMO_CONTRACT_DB, граф дополняется этими packages и их Machine/Flow/TypeRef declarations, SemanticPort, Principal, Capability, State, Input, Guard, Output и exact import связями. Для опубликованного protobuf index инспекция теперь показывает каждый TypeRef, его field numbers, origin и descriptor digest. Для typedEffects Rust повторно компилирует strict Feature и выдаёт отдельный kernel профиль Machine с GuardSet, evidence components, OutcomeRoute и typed binding. Узлы kernel профиля связаны с исходником и Machine declaration, но имеют собственную identity: совпадение имени само по себе не склеивает контракты. TypeRef и binding показывают проверенные связи внутри опубликованного package, а не совершившийся эффект. Новый package появляется без правки demo loaders. Отдельный статус PUBLISHED_CONTRACT означает проверенную публикацию в выбранной базе; inspection не утверждает release binding, исполнение или внешнюю доставку. Отсутствующий либо повреждённый package вызывает ошибку вместо догадки. Путь к базе задаётся оператором сервера, браузер не выбирает произвольный файл. Порядок запуска описан в runbook.

Следом расположен атлас строгих деклараций. Он обходит локальные fixtures/ и tests/bdd/features/protocol/ и сейчас показывает 23 compiler-checked source variants: 11 Machine, 8 Flow, один native TypeSpec и три type extension. Можно выбрать любой вариант по имени, профилю или пути; inspector раскрывает исходный Feature, результат Rust compiler, State/routes, Flow projection/CEL либо поля и base TypeRef. React Flow показывает связи Flow с каждым Machine-вариантом, чей скомпилированный Input/Output symbol совпал с SemanticPort. Эти пунктирные связи имеют статус SOURCE_SYMBOL_CANDIDATE: они помогают исследовать mesh, но не подтверждают совместимость package, TypeRef, release или доставку. Для трёх определений, чьи exact source bytes встроены в локальные digest-checked packages, атлас отдельно показывает package/release digest и ссылку на проверку и запуск. Это связь исходника с release, а не утверждение об исполнении выбранной декларации. Для extension с base TypeRef вне локального source corpus атлас показывает внешнюю ссылку, не приписывая ей ложного владельца. Навигация вверху страницы ведёт от атласа к pinned assembly, отдельным Machine, kernel, типам, graph release, read entity и исполнению.

Machine Studio под mesh раскрывает каждый из четырёх release-scoped Machine contracts: State, Principal, Capability, Input, Guard, Transition, Output, proof, SemanticPort, protobuf TypeRef и два вида исходника там, где runtime Feature отличается от binding Feature. Два ServiceCase не объединяются по имени: у них разные release pins. После запуска Studio показывает упорядоченные Commitment, State и Trace head из проверенной Rust истории. Карточка cel-map-v2 показывает исходный Flow Feature, выражение, engine и expression digest, а после запуска — фактически вычисленный Input. Это Flow projection; Guard целевой Machine остаётся отдельной проверкой. Для bounded graph cycle есть ещё закрытый tyapkin-condition-v1 profile; ни один из этих профилей не делает CEL универсальным CAP Guard.

Каталог Machine дополняет эти закреплённые сборки всем локальным protocol corpus: сейчас 11 строгих Machine Feature в пяти именах. Сервер обходит fixtures/ и tests/bdd/features/protocol/, компилирует каждый найденный Machine-кандидат через Rust capkin compile или kernel-inspect-json и показывает отдельный вариант для каждого source path/profile/digest. На React Flow и в карточках видны все State, Input, Guard, Transition, Output, Principal и Capability; composite профиль дополнительно показывает все три исхода, evidence components и execution bindings. Повреждённый Machine-кандидат вызывает явную ошибку каталога. Статус COMPILED_LOCAL_SOURCE означает только проверенный исходник: package publication, TypeRef/Port из binding и Trace доказываются отдельно в release-scoped Studio. Так каталог не выдаёт 11 исходников за 11 опубликованных или исполняемых Machine.

Kernel Machine Studio читает структурированный JSON непосредственно из Rust kernel compiler. Для EvidenceMachine он показывает оба компонента GuardSet и три маршрута PASS/DENY/INDETERMINATE на одном графе. После локального запуска можно увидеть отдельные State и Trace head каждого исхода, сохранённые evidence, два OutputIntent, две Observation и два ExecutionReceipt. Для типизированной ServiceCase показаны TypedEffectBinding, protobuf TypeRef и typed receipt. Typed effect сейчас не записывает отдельную generic Observation; это явно указано в UI. Оба исполнения локальные, а источником деталей служат compiler и Rust host.

Type Registry Studio показывает другой разрез тех же contract primitives: точные import refs, package owners, общий tyapkin-type-ir-v1, protobuf TypeRef, native TypeSpec и extension. Два локальных контекста собираются из фиксированных fixtures: Rust компилирует Type Feature, ContractStore публикует packages, затем Rust type-ir-check проверяет import closure. React Flow показывает связь package → TypeRef → field и extension → base; inspector различает владельца типа и владельца каждого унаследованного поля. Временный SQLite registry удаляется после построения read model. Этот экран не публикует произвольный пользовательский package.

Flow Graph Studio показывает release-level маршрут между независимыми Machine. Сервер вызывает Rust capkin flow-graph-check для четырёх закреплённых release: fanout, CEL v2 projection, bounded cycle и scoped cycle. Он сверяет SHA-256 каждого binding IR и runtime schema, затем повторно компилирует исходный строгий Flow Feature. На графе видны source Output Port, Flow, target Input Port и обе направленные стороны цикла. Inspector раскрывает точное CEL выражение, engine, поля, scope, source Feature и digest. Для cycle отдельно показаны maxIterations = 3 и два typed CEL exit condition. После запуска интерфейс связывает delivery keys, Trace heads, terminal evidence и transport receipt с digest этого же release. Accepted transport receipt не выдаётся за target Transition. Это проверенная read-only визуализация локальных fixtures; браузер не интерпретирует CEL и не создаёт новый Flow.

Entity Studio показывает, что одно имя AssetCase.View@1 ещё не определяет одну и ту же read entity. Вариант v4 получает asset_id из committed Asset Output и case_id из committed ServiceCase Input после причинной Flow delivery. Вариант v5 берёт оба поля из committed Output двух Machine. Сервер публикует exact packages во временный ContractStore и проверяет оба manifest командой Rust federation-check --db; UI допускает только совпавшие с pinned resolution bytes. React Flow связывает Machine, SemanticPort, field owner package, зависимость requires и read entity. Inspector показывает исходную декларацию и checked resolution. Кнопки запускают локальный Rust host: для v4 видны fresh/stale/unknown, для v5 — срезы с одним и двумя Output, expiry и чужим tenant. Значение, причина, возраст, source commitment и Trace head берутся из FederatedReadStore; read projection не меняет State и Trace исходных Machine.

Статус: проверенный локальный publication/runtime срез, 8 октября 2026. Rust capkin flow-compile компилирует field-copy-v1, flow-v2 и opt-in flow-cel-v1. Для federated read entity есть локальный preflight и ограниченная materialization из проверенной causal доставки; v2 исполняется как отдельный scoped Flow и как маршрут mixed strict graph release-v3 с независимым CEL Flow в локальном reference host. CEL profile допускает закрытое map expression, не весь язык CEL. Поэлементный аудит находится в openspec/changes/archive/2026-10-08-capkin-federation-imports-flow/acceptance-audit.md. Локальные publication, bounded Flow/CEL, federation read и Playwright demo проверены в текущем профиле. Hosted CI и branch protection выведены из scope локальной приёмки; внешние adapters и доверенные executor keys ещё не приняты. Промежуточные снимки ниже сохраняют историю срезов; актуальная матрица приёмки находится в OpenSpec. Узкий capkin imports-check проверяет offline sidecar graph: точные package ID/version/digest, alias, visibility, отсутствие циклов и diamond с одним исходным символом. Он сверяет SHA-256 bytes локальных tyapkin-package-exports-v1 artifacts, но пока не загружает полные published packages из registry в старом v1 профиле. Opt-in imports-check --db разрешает v2 graph из точных bytes в локальном ContractStore. Локальная команда pnpm check:capkin:bindings уже проверяет два Machine, protobuf binding и три варианта узкого Flow; правила PortBinding, TypeRef provenance и projection теперь проверяет Rust capkin binding-compile. Команда не вызывает RPC. Полный порядок и проверяемые требования зафиксированы в openspec/changes/archive/2026-10-08-capkin-federation-imports-flow/ и docs/architecture/capkin-federation-flow.md.

Источники фактов

.proto / registry commit → pinned descriptors → внешний TypeRef index
Capkin package          → Machine, SemanticPort, Flow, Guard, Trace
Binding                → semantic ID ↔ protobuf message / RPC
Allium host             → endpoint, outbox, RPC adapter, Observation

.proto определяет поля, сообщения и сервисы; Tyapkin не переписывает их и не генерирует .proto в первом schema-adapter profile. Capkin определяет поведение и устойчивый порт ServiceCase.OpenCase@1. Привязка может быть помечена protobuf custom options represents_type/implements_port или задана sidecar для чужой схемы. Компилятор отклоняет конфликт этих двух источников привязки. Allium выбирает версии слоёв и исполняет проверенную семантику Tyapkin, не создавая второй язык Flow.

Текущий пример: proto/tyapkin/options/v1/binding.proto, proto/tyapkin/fixture/port_binding/v1/service.proto, fixtures/port-binding-v1/contract.json и fixtures/port-binding-v1/compiled.json, а также tests/bdd/features/protocol/port-binding-flow.feature. Digest descriptor закреплён в fixture manifest. Замена имени RPC при прежнем alias проходит проверку, а несовместимый request type — нет. Компилированный binding IR сравнивается с сохранённым fixture при каждом запуске checker. В IR есть types[] с semantic ID, именем protobuf message и digest descriptor. Это индекс происхождения, а не второе определение полей и ещё не полный Type IR. binding-build дополнительно выдаёт отдельный tyapkin-protobuf-type-index-v1: для каждого привязанного semantic типа — owner: protobuf, digest descriptor, исходный .proto файл и message FQN, а также номера и формы полей, прочитанные из descriptor. Эти поля не объявляются вторично в Capkin. Индекс и его SHA-256 закреплены в fixtures/port-binding-v1/type-index.json и fixtures/port-binding-cel-v1/type-index.json. Rust отклоняет повторные или нулевые номера полей. Индекс пока охватывает только типы данного binding manifest и не является общим registry всех типов. При локальной сборке отдельный checker декодирует бинарный FileDescriptorSet и независимо сверяет index/IR с origin, каждым полем, represents_type, RPC method/input и implements_port. Отрицательные тесты меняют поле, oneof, alias, method и message. Такой же preflight запускается до сборки двух store composition fixtures; публикация старого формата v2 через ContractStore не выполняет эту бинарную проверку. Для нового opt-in формата tyapkin-contract-package-v3-protobuf это ограничение снято в части TypeRef: Machine package должен содержать types/protobuf-index.json, binary schema/descriptor.binpb и bindings/ports.json. ContractStore::publish через Rust prost-reflect сверяет digest, file/message, поля, presence/oneof, represents_type, port direction, payload TypeRef, RPC Input и implements_port до записи. Пересчитанный SHA-256 подменённого index/binding не обходит проверку. При публикации следующей v3 версии того же ID поле compatibilityBaseline указывает exact version/digest последней сохранённой версии. Store сверяет добавочную совместимость прежних exports, TypeRef field IDs/forms, binary field/oneof/method declarations и PortBinding. Новое поле допустимо; изменение прежнего поля или метода отклоняется даже при согласованно пересчитанном descriptor и index. Legacy → v3 и breaking переход без migration policy пока закрыты. Flow declaration в новом v3/v4 package при прямом publish сверяется с точными импортированными checked Machine packages: source должен быть Output, target — Input, payload TypeRef и root binding IR обязаны совпасть с владельцами. Field-copy покрывает все target scalar fields с совместимым типом; CEL map проверяется по полям обоих protobuf типов, включая отказ неизвестного source field и несовпавшего expression. Это publication preflight; он не исполняет Flow v2 и не включает внешний transport. Для нескольких Flow один checked package указывает отдельный binding artifact в каждой Flow declaration. Публикация отвергает неявный или общий binding, повторную пару source/target и несовпадение CEL engine pin. Следующая additive версия того же Flow export обязана сохранить его compiled IR и binding bytes; изменение expression требует отдельной version/migration policy. Для opt-in Flow@N прямой v5 parallel publish проверяет Flow@1 → Flow@2, exact baseline, полный replacement map и совпадение compiled Flow с binding IR. Прежний unversioned Flow не получает скрытого переименования: @1 публикуется и активируется явно. Несколько опубликованных Flow не активируются автоматически: DAG/cycle gate выполняется для graph release. При v3 публикации checked import closure проверяется по exact сохранённым пакетам: один semantic TypeRef не может получить второго package owner. Межпакетная binding compatibility и полный CEL gate остаются открытыми; v1/v2 bytes/readers не изменены. Для exact опубликованных пакетов локальный types-check --db строит read-only tyapkin-type-registry-v1 по всему v2 import graph. В fixtures/composition-store-v3/types-resolved.json видны четыре semantic TypeRef, package owner, descriptor origin и номера полей. Конфликт двух package owners или повтор номера/имени поля отвергается. Это индекс опубликованных artifacts: он пока не сверяет поля заново с бинарным descriptor, не охватывает Tyapkin-native types и не является полным Type IR. Composition-v3 открывает Flow только после такой проверки всего import graph. Checked package v2 с вложенным protobuf индексом также проходит проверку его формы, provenance и уникальных field identities до публикации.

Новый opt-in tyapkin-contract-package-v4-types публикует собственный тип Tyapkin из строгого Gherkin-compatible профиля type-native-v1. capkin type-compile --file fixtures/native-type-v1/case-note.feature выдаёт tyapkin-native-type-ir-v1 с module owner, source digest и стабильными номерами полей. ContractStore::publish повторно компилирует source и сравнивает IR до записи, а затем проверяет одного владельца TypeRef во всём exact import closure. Следующая версия v4 требует compatibilityBaseline последней версии и сохраняет прежние поля; новые поля допустимы. Прежние v1/v2/v3 package bytes не меняются.

Для явного breaking перехода добавлен opt-in tyapkin-contract-package-v5-parallel. Его migrationPolicy с форматом tyapkin-parallel-cutover-v1 проверяется при прямом publish: новая major version N.0.0, exact compatibilityBaseline, полное отображение прежних публичных TypeRef/port identities на новые ID с тем же stem и большим @version, без повторного использования старых ID. Обязательные значения existingInstances: retain-baseline и consumerCutover: explicit-release описывают параллельное сосуществование. Проверенный fixture публикует native CaseNote@1 → CaseNote@2 с новым типом поля и затем additive 2.0.1; старый import graph после публикации выдаёт прежний Type IR. Store не переключает live instances и не выполняет State/payload conversion. Выбор release и проверка фактического cutover остаётся отдельной runtime задачей. Второй публичный fixture публикует параллельную v5 protobuf ServiceCase schema/ports с новыми @2 IDs и дополнительным bool field: binary descriptor и TypeRef index проверяются независимо, incomplete replacement map, stale baseline и пересчитанный index drift отклоняются. Старый protobuf Type IR после этого по-прежнему разрешается из прежних exact bytes.

capkin type-ir-check --file <import-graph-v2.json> --db <store.sqlite> строит read-only tyapkin-type-ir-v1 из опубликованных native TypeSpec и protobuf TypeRef indexes. У каждого поля видны number, type, presence и origin package. Это общий производный read model, а не новый source of truth: native fields принадлежат TypeSpec, protobuf fields — descriptor. Строгий type-extension-v1 Feature объявляет только новые scalar fields, base TypeRef и alias точного импортированного protobuf, native или extension owner. Такой v4-types package публикуется после проверки base index/TypeSpec из checked v3/v4 owner и его bounded base chain: номер или имя inherited field нельзя повторить. Общий type-ir-check показывает inherited fields, added fields и их владельцев. Следующая версия package пинит exact baseline и сохраняет ранее добавленные поля. Этот срез поддерживает прямой protobuf/native/extension base с ограничением глубины цепочки. При additive публикации новый native или extension @N требует опубликованный @N-1 того же owner/kind и сохраняет прежние поля; extension также сохраняет exact pinned base. Первая версия типа — @1; пропуск версии и изменение field identity отвергаются через contract-publish. Старый read-only Type IR остаётся читаемым после restart. Межпакетная binding compatibility, breaking migration и полный Flow/CEL runtime gate ещё не приняты. Локальный ContractStore также закрепляет native/extension TypeSpec за одним package ID на уровне всей SQLite registry. Второй независимый package с тем же редактируемым ID типа не публикуется; при открытии старого store индекс восстанавливается из проверенных immutable package bytes. Protobuf payload TypeRef из общего descriptor может встречаться в независимых Machine packages; его origin и imports проверяются отдельно. Native/extension TypeSpec не может занять уже опубликованный protobuf ID типа. Историческая коллизия в старой SQLite базе не удаляет package: backfill сохраняет чтение обоих, отмечает конфликт и блокирует новую публикацию на этот ID. Второй store-backed release fixtures/asset-store-composition-v4/ собирает Asset, ServiceCase и field-copy Flow из трёх checked packages. Он использует тот же import/type preflight и локально исполняет причинный Asset Output → ServiceCase Input; отдельный release digest сохраняет delivery identity при restart. CEL composition-v3 остаётся прежним. В release-v5.json host закрепляет один документ: его digest, пути и SHA-256 composition/runtime schema, root package. Локальный FlowRuntime::open_release_with_store проверяет этот документ и открывает тот же Flow из данных ContractStore. V5 delivery key закреплён exact bytes release, поэтому повтор после restart распознаётся, а v4 key остаётся прежним. Старый release-v4 продолжает работать. Исторический CapkinTypeIrV1 остаётся форматом старого Allium aggregation package и его reader; новый external TypeRef берёт поля из protobuf descriptor. Это согласовано с ProCoffee schema-first правилом: protobuf владеет wire shape, а Capkin — поведением и смысловыми портами. Старые package bytes не перекомпилируются. Rust отклоняет двойную привязку message/method, несовместимый RPC Input, неполную projection и oneof/explicit presence, которые field-copy-v1 пока не моделирует. buf breaking сравнивает схему с отдельным v1 descriptor baseline, а fixture digest закрепляет точные байты текущего descriptor. Это два разных gate: совместимость и неизменность выбранной версии.

flow-v2 добавляет явную policy: committed Output trigger, tenant scope из source Trace, object scope по строковому полю Output, authority source Output principal, tenant/object correlation, idempotency по source commitment и target port, удержание неизвестного исхода, остановку при DENY, максимум пять попыток и удержание после их исчерпания. Пример находится в tests/bdd/features/protocol/port-binding-flow-v2.feature; manifest fixtures/port-binding-v2/contract.json явно выбирает flow-v2 и сравнивает IR с compiled.json. Компилятор отклоняет несовпадение профиля, отсутствующее scope поле и небезграничный retry. Runtime требует отдельный schema marker v3 и scoped instances.

Локальное исполнение v1

cap-cli::FlowRuntime принимает сохранённый compiled.json v1 и отдельно прикреплённый runtime-schema.json с digest из host release. Schema artifact строится Rust binding compiler из того же protobuf descriptor и содержит digest binding IR и source/target field shapes. Runtime проверяет оба digest, точные поля и поддерживаемые scalar types локального canonical JSON profile; неверный тип или лишнее поле отвергается до target apply. Runtime проверяет commitment исходной Machine вместе с intent_id, SHA-256 точных байтов Output и историю её Trace. Затем копирует объявленные поля в локальный JSON Input, сохраняет причинную доставку в SQLite и через KernelFlowTarget вызывает apply целевой Machine. У цели отдельные Guard, State и Trace. Повторная подача после рестарта возвращает прежний результат. Потерянный ответ помечается unknown; lookup того же command ID может подтвердить уже записанный целевой переход. Этот путь проверен Rust fixture с двумя Machine, чьи контракты читаются из отдельных строгих Capkin files port-binding-service-case-kernel.feature и port-binding-scheduling-kernel.feature, включая ошибочные байты и подменённый outbox commitment.

Для host со своим хранилищем добавлен opt-in Rust FlowHostPortV1 и FlowRuntime::enqueue_with_ports_v1. Порт даёт read-only Machine contract, IR digest, replay-проверенный source Output, replay-проверенный target checkpoint и scope. Прежний enqueue(&KernelHost, &KernelHost, …) вызывает тот же путь через реализацию порта для KernelHost. Conformance fixture сравнивает delivery bytes/key, повтор после restart и отказы при искажённом IR, Output hash и tenant scope. Реализация порта должна быть доверенной: произвольный клиентский JSON не становится проверенным Trace. Это Rust API в локальном исходнике, а не опубликованный process/RPC ABI, Allium State service или доказанная внешняя доставка. При DENY delivery завершается без целевого Transition; при INDETERMINATE удерживается unknown без целевого Transition. После рестарта незавершённая отправка сначала становится unknown и проверяется по прежнему command ID; она не отправляется повторно вслепую. Перед enqueue runtime сравнивает semantic shape обеих запущенных Machine с binding IR: principals, capabilities, states, guards и Input/Output. Кроме того, schema artifact пинит точные CapIrV1 digests обеих Machine; совпадения Machine ID недостаточно. Отдельный Rust test сверяет digest legacy Machine Feature и строгого kernel Feature для каждого слоя.

Runtime v1 доверяет выбранному host и его pinned schema digest; локальный транспорт использует canonical JSON и поддерживает string, bool и 32-битные целые scalar fields. Это reference adapter, не protobuf wire codec, не gRPC endpoint и не публикация package. Tenant scope, объектная область и retry policy из v2 исполняются только для нового opt-in локального профиля; v1 путь остаётся без scope.

Второй fixture fixtures/asset-case-v1/ исполняет независимые Machine Asset и ServiceCase. Asset.AssetContextChanged@1 несёт asset_id и case_id; Flow field-copy переносит оба поля в ServiceCase.OpenCase@2. Каждая Machine имеет собственные Guard, State и Trace, а protobuf descriptor пинит четыре semantic ports и номера полей. pnpm check:capkin:asset-case проверяет Buf lint/breaking, официальный Gherkin AST, exact binding IR, TypeRef index и runtime schema; Rust tests проверяют causal enqueue, целевой commit, restart/replay и отказ при изменённых source bytes, DENY или INDETERMINATE. Это локальное исполнение через JSON/SQLite host без живого RPC и без v2 tenant policy.

Отдельный flow-cel-v1 исполняется в том же локальном JSON/SQLite host. tests/bdd/features/protocol/port-binding-flow-cel.feature содержит Gherkin DocString с типом cel:

And project target Input with CEL
  """cel
  {"case_id": output.case_id, "reason": "maintenance"}
  """

Source Output ServiceCase.CaseOpened@1 содержит case_id, а target Input Scheduling.ScheduleVisit@2 содержит case_id и reason. Target port и protobuf payload получили отдельную версию. Rust проверяет полное покрытие target полей, типы, форму выражения и лимиты до выпуска IR v3. Выражение и версия движка cel-rust/0.14.5/tyapkin-projection-v1 закреплены digest. Runtime повторно проверяет CEL при открытии host и вычисляет target Input после verified Output intent, сохраняя причинный outbox. Fixture отдельно пинит descriptor, binding IR и runtime schema. Текущий subset ограничен map с не более чем 64 полями и 4096 байтами expression; вызовы функций и comprehensions отклоняются. Это локальный reference adapter без внешнего RPC. Compiler использует CelProjectionPort, а runtime при открытии Flow сверяет engine_id port с pinned IR до вызова checker. Замена backend означает новый versioned engine ID и отдельную проверку; текущие IR bytes не меняются. Семь общих golden vectors в fixtures/cel-projection-v1/golden.json проверяются Rust CEL engine и независимым Node reference evaluator через pnpm check:capkin:cel-golden. Vectors фиксируют canonical JSON результата, ошибки типов, отсутствие полей и отказ вызова функции. Полный CEL type system и произвольные protobuf shapes этим subset не объявляются.

Отдельный # @profile:flow-cel-v2 сохраняет прежний v1 reader и создаёт новые IR v4 / runtime schema v4 с engine cel-rust/0.14.5/tyapkin-projection-v2. Он разрешает проверенные по protobuf scalar полям conditional, equality/order, boolean operators и строковое сложение. Checker отказывает вызовам функций, collections, неизвестным полям и несовместимым типам ветвей. Предельные значения: 4096 байт expression, 64 поля, 256 AST nodes, глубина 16, по 65 536 байт source и target payload. Проверка повторяется при ContractStore::publish и FlowRuntime::open; локальный E2E создал target Transition и повтор после restart без второй доставки. Девять общих golden vectors исполняются Rust и независимым Node parser/evaluator. Этот профиль остаётся scalar-map; protobuf nested/repeated shapes и общий CEL type system открыты.

Feature: CaseToSchedule

# @registry:FLOW-CASE-SCHEDULE-002
# @profile:flow-v2

  Background:
    Given flow CaseToSchedule

  Scenario: FLOW-CASE-SCHEDULE-002
    When output ServiceCase.CaseOpened@1 is committed
    Then submit input Scheduling.ScheduleVisit@1
    And project field case_id to case_id
    And causation references the source commitment
    And tenant scope is inherited from the source trace
    And target authority uses the source Output principal
    And correlation uses tenant and object scope
    And object scope is source field case_id
    And idempotency key uses source commitment and target port
    And unknown outcome is held
    And denied outcome stops
    And transient failure allows at most 3 attempts
    And exhausted attempts are held

Этот v2 профиль теперь исполняется локально через отдельный tyapkin-flow-runtime-schema-v3. Host создаёт экземпляры через create_scoped_instance(instance_id, tenant_id, object_id); исходный Output и целевой Input должны относиться к тем же tenant/object, а adapter проверяет principal исходного Output и target capability до apply. Transient разрешает ограниченный повтор, Unknown удерживается до lookup того же command ID, Denied останавливает маршрут. Scope и authority хранятся рядом с причинной доставкой в SQLite. Для held и denied новый локальный FlowTerminalEvidence записывается атомарно с terminal outbox status. verify_terminal_evidence(source, target, key) сверяет digest документа, source Output commitment, обе Machine Trace, первоначальный target head, scope, authority и число попыток; изменённые bytes и чужой Trace отвергаются. Это локальная reference реализация: документ не является подписанным external receipt и не доказывает доставку внешнему transport. Отдельный schema-first DeliveryRequestV1 и DeliveryReceiptV1 из tyapkin.flow.transport.v1 пинят Flow/binding, source commitment, exact target Input bytes и их hash, scope, authority и попытку. Descriptor gate сверяет wire поля с Rust codec; независимые Rust/Node encoders дают одинаковые request/receipt bytes и request digest на общем golden vector. FlowTransportPort разделяет submit и lookup; lost response остаётся unknown, а найденный signed receipt атомарно сохраняется со статусом transport-accepted, transport-denied или transport-failed. Verifier проверяет точные protobuf bytes, adapter ID и attestation через доверенный host authenticator после restart. Adapter ID пинится до submit; маршрут с unknown нельзя переключить на локальный FlowTarget без signed receipt. Даже transport-accepted не меняет target CAP State и не притворяется target Transition. Локальный conformance test покрывает три исхода, потерянный ответ, replay, чужой adapter, подмену подписи/попытки/bytes; production gRPC adapter и конфигурация доверенных ключей остаются открытыми. fixtures/flow-graph-v1/release-scoped-v3.json показывает этот v2 Flow вместе с прежним CEL маршрутом под одним strict graph release. Оба получают отдельные causal deliveries и проходят собственные target Guard; после restart ключи не меняются. Scope v2 не приписывается CEL маршруту.

Какие отношения надо различать

ОтношениеЧто получает слой
Contract importИмена и типы экспортированного пакета по ID/version/digest
Runtime depends onТребование наличия способности другого слоя
Type extendsНовый тип с собственными полями; исходный тип неизменен
Federated contributesПоле общей read entity с собственным field owner и provenance
Flow Output → InputНовую причинную команду для другой Machine

Текущий import fixture — fixtures/import-graph-v1/diamond.json. capkin imports-check --file fixtures/import-graph-v1/diamond.json возвращает importedSymbols с исходным package для каждого имени; две ветви diamond ссылаются на один example.shared.Event. pnpm check:capkin:imports сверяет результат с resolved.json и входит в локальный pnpm quality. Это проверка графа зависимостей; она ещё не подключена к generic Machine/Flow compiler. Отдельный fixtures/import-graph-store-v2/graph.json проверяет те же alias, visibility и diamond через четыре immutable package в ContractStore. pnpm check:capkin:store-imports отвергает changed digest и import declaration; v1 fixture не меняется.

Для прикладной сборки есть следующий локальный gate: capkin composition-check --file fixtures/composition-v1/manifest.json. Он связывает четыре SemanticPort двух независимых packages с pinned binding IR v1, сверяет digests и выводит resolved.json с исходным package каждого порта. CaseOpened → ScheduleVisit проходит только если оба Flow endpoints импортированы. Ошибочный import alias, неверный экспорт или изменённый byte binding IR отклоняется. pnpm check:capkin:composition входит в pnpm quality. FlowRuntime::open_composed может открыть проверенную композицию в локальном v1 JSON/SQLite host. Его outbox keys привязаны к digest композиции, так что два release с одинаковым binding IR не совпадают по delivery identity. Локальный release.json хранит отдельно проверяемые pins manifest и runtime schema. Код слоя из composition не генерируется; внешний transport и scoped v2 release через эту v1 композицию не приняты.

fixtures/composition-v2/ — отдельный pinned release для CEL Flow. Он импортирует ServiceCase и Scheduling версии 2.0.0, требует экспорт Scheduling.ScheduleVisit@2, привязывает все четыре SemanticPort к их исходным package artifacts и включает неизменённый compiled CEL IR v3. FlowRuntime::open_composed принимает exact manifest digest из release, повторно проверяет binding/schema и строит Input с reason через CEL. Delivery key отличается от прямого открытия того же IR. Старый composition-v1 и field-copy release сохраняют прежние байты.

fixtures/composition-store-v3/ связывает ту же CEL семантику с полными опубликованными tyapkin-contract-package-v2 bytes. Каждый такой пакет содержит исходный .feature, точный compiler IR и declaration exports; contract-publish повторно компилирует исходник и проверяет, что Machine публикует только свои Input/Output, а Flow использует импортированные порты. Checked package принимается после публикации каждой импортируемой версии с тем же digest; отсутствие зависимости или изменённый digest отвергаются. Команда capkin composition-check --file fixtures/composition-store-v3/manifest.json --db <registry.sqlite> требует v2 import graph, проверяет pinned digests пакетов, exports и declarations, а затем полный набор портов и оба конца Flow. FlowRuntime::open_composed_with_store повторяет эти проверки при открытии release; отдельный digest manifest формирует delivery identity. Корневой package закрепляет тот же binding IR как собственный artifact: проверка сравнивает его точные bytes с выбранным IR, затем сопоставляет скомпилированный из Flow source ID, scenario, endpoints и CEL expression с binding. Подмена связи между source Flow и binding отклоняется. Каждый Machine contract из binding также сравнивается с точным compiled IR его импортированного checked package; порты одной Machine должны иметь одного package owner. В каждом Machine package лежит собственный subset protobuf TypeRef index; loader сверяет payload TypeRef каждого порта, descriptor digest и message origin с binding IR. Совпадения имён портов недостаточно. Локальный тест проверяет восстановление после повторного открытия и отказ при отсутствующем пакете. pnpm check:capkin:store-composition сверяет resolution byte-for-byte и проверяет отрицательный digest drift. Это локальная сборка из опубликованных данных контракта: package source не загружается как исполняемый код. Этот узкий Rust compiler gate не заменяет полный publish gate с независимой проверкой protobuf compatibility, CEL cost/type environment и всех runtime policies.

При federation нужны entity key, owner каждого поля и список внешних полей, необходимых для вычисления. При Flow нужны trigger, pinned source/target types, projection, tenant/object scope, authority, causation, unknown/failure route, идемпотентность и ограничение повторов. Никакой слой не меняет State соседа.

Первый локальный preflight есть в fixtures/composition-v2/entity.json: capkin federation-check проверяет Dispatch.WorkItem@1 с ключом case_id. Поле case_id принадлежит пакету ServiceCase, а reason — Scheduling; reason явно требует внешний case_id. Владелец выводится из проверенного import origin, тип — из pinned protobuf runtime schema. Результат entity-resolved.json хранит owner ID/version/digest, source port/field, type и dependencies. Валидатор отклоняет два владельца одного поля, неизвестный source field, отсутствующую или циклическую зависимость, неверный ключ и изменённые pins. pnpm check:capkin:federation сравнивает результат с fixture в локальном quality gate. Этот v1 профиль проверяет только декларацию read entity; значения появляются в отдельном v4 профиле.

Отдельный fixtures/composition-v2/entity-typed.json выбирает v2 preflight. Он дополнительно пинит type-index.json, сверяет его descriptor/message и форму полей с binding IR и runtime schema и сохраняет protobuf field number в entity-typed-resolved.json. Изменённый digest или type shape отклоняется. Предыдущий v1 manifest и resolution остались без изменений. V2 также пока только проверяет контракт read entity, не материализует данные. fixtures/asset-store-composition-v4/entity.json выбирает typed v3 preflight: capkin federation-check --db берёт TypeRef владельцев и номера полей из опубликованного import closure, сверяет их с binding и runtime schema. Отдельный type-index.json в entity manifest больше не нужен. AssetCase.View@1 показывает поле Asset и поле ServiceCase с явной внешней зависимостью; результат закреплён в entity-resolved.json. V3 пока не хранит значения, не отслеживает freshness и не открывает query API.

fixtures/asset-store-composition-v4/entity-values.json вводит opt-in v4 read entity с pinned freshness.maxAgeSeconds. Локальный FederatedReadStore::materialize_flow принимает завершённую causal доставку, повторно проверяет исходный Output и целевой Input по их независимым Trace, проверяет точные payload hashes, tenant/object scope, field types и key. Он сохраняет read snapshot отдельно от Machine State; каждое поле содержит owner package и точный source commitment. query показывает fresh, stale или unknown и возраст. Запрос с другим tenant/object получает только unknown поля. Пропавшая зависимость делает зависимое поле unknown; просроченное значение не возвращается как актуальное. Локальный тест проверяет положительный путь, чужой tenant, expiry, подменённый payload, restart и неизменность обеих Trace. Это read projection одного Asset Output → ServiceCase Input. Новый профиль entity-outputs.json берёт два независимых committed Outputs: Asset.AssetContextChanged@1 и ServiceCase.CaseOpened@1. Их TypeRef, номера полей и owners разрешаются из published import closure. materialize_outputs повторно проверяет каждую Trace и exact payload, принимает только одинаковый tenant/object, сохраняет отдельный observedAt каждого поля. Отсутствие второго Output даёт unknown, а просроченный required field делает зависимое поле unknown даже если его собственный источник ещё свежий. Внешние observations и hosted query endpoint остаются отдельной интеграцией.

Отдельный capkin type-extension-check с manifest fixtures/type-extension-v1/manifest.json проверяет additive extension protobuf TypeRef. Новый ServiceCase.CaseOpenedWithSource@1 получает owner example.dispatch@1, наследует case_id с номером 1 и добавляет source с номером 2. База загружается по SHA-256 из производного индекса и остаётся владением .proto. Компилятор отклоняет повтор имени/номера, неизвестный base type, изменённый pin и неподдержанный scalar shape; resolved.json сравнивается byte-for-byte в pnpm quality. manifest-v2.json создаёт @2 поверх pinned resolved.json версии @1: checker требует соседнюю версию и сохранение каждого прежнего field number/name/type/owner; удаление, изменение типа и подмена pin отклоняются. Это локальный sidecar profile: новый тип ещё не связан с портом, runtime или общим registry.

Локальное хранение контрактных байтов

ContractStore в cap-cli сохраняет exact bytes версионированного tyapkin-contract-package-v1/v2/v3-protobuf по ID/version в SQLite. Самодостаточный пакет перечисляет exports/import refs и содержит артефакты с SHA-256 и вложенными bytes. Команда capkin contract-publish с --file и --db возвращает ID/version/digest. Повтор тех же байтов идемпотентен; изменённые байты под той же версией и повреждённый artifact отклоняются. Fixture fixtures/contract-store-v1/package.json содержит Machine source и производный protobuf TypeRef index; pnpm check:capkin:contract-store проверяет сборку fixture и CLI publication. Чтение после restart повторно проверяет package integrity.

V1 пакет проверяет структурную целостность. V2 пакет дополнительно требует source, скомпилированный Rust IR и проверенные semantic exports до записи. V3 protobuf сохраняет эти требования и для Machine независимо проверяет embedded binary descriptor против TypeRef index и package PortBinding при самой публикации. При следующей версии того же ID compatibilityBaseline пинит последний package, а additive check сохраняет поля и semantic ports. V2 import resolver читает exact bytes, но не активирует Machine/Flow и не выполняет полный publish gate. Allium по-прежнему владеет собственным каталогом LayerPackage и ApplicationRelease.

capkin flow-graph-check --file fixtures/flow-graph-v1/manifest.json проверяет несколько pinned binding IR перед сборкой. Текущий профиль разрешает только DAG, проверяет направление портов, уникальность Flow и маршрутов, одинаковый compiled contract общего Machine ID и выдаёт детерминированный порядок Machine. Два Flow ServiceCase → Scheduling проходят; добавление скомпилированного обратного Scheduling → ServiceCase отклоняется с путём цикла. Изменение digest или повтор маршрута также отклоняются. pnpm check:capkin:flow-graph сравнивает resolution с fixture и проверяет отрицательный граф. Этот gate не активирует Flows и не исполняет ограниченные циклы. Политика выбора между двумя маршрутами от одного Output задаётся в opt-in fixtures/flow-graph-v1/release-v2.json: fanout: all означает постановку каждого подходящего маршрута в собственный локальный outbox. LocalFlowGraphRuntime::open сверяет SHA-256 самого release, каждого binding IR и runtime schema, проверяет общий Machine contract и DAG, затем открывает два прежних FlowRuntime. enqueue_all выбирает маршруты по проверенному committed Output, возвращает отдельный результат каждого Flow и узнаёт повтор после restart. Ключи доставки зависят от exact release bytes и соответствующего binding IR, поэтому отделены от прямого Flow reader. Частичный успех возможен и виден вызывающему host; автоматический dispatch, общий атомарный commit и внешние эффекты здесь не реализованы. Отдельный opt-in release-v4 исполняет bounded cycle; старый graph-v1 resolution не меняется. Локальный SQLite pin закрепляет verified source Output за одним graph release digest. Host может вызвать pin_source_output при создании Output; enqueue_all повторно проверяет pin перед постановкой. При попытке провести этот же Output через изменённый release runtime отказывает до новой доставки. Pin распространяется и на Output, для которого прежний release не имел маршрута: новый граф не должен подхватывать его задним числом. Если host не сделал pin до переключения, ещё не обработанный старый Output может попасть в новый release. Атомарную активацию и rollback новых ApplicationRelease должен реализовать Allium host.

Для этой границы добавлен opt-in tyapkin-local-flow-graph-release-v3. Host открывает KernelHost с exact graph digest через with_graph_release до apply; новый OutputIntent и его release pin записываются в одной SQLite transaction. V3 graph проверяет этот pin до enqueue и не принимает Output, созданный без pin либо под другим release, включая Output без текущего маршрута. Повтор команды после смены выбранного release сохраняет старый pin. release-v2 и его delivery key bytes не изменены; v3 имеет отдельный domain separator. Выбор активного ApplicationRelease и атомарный rollback всё ещё принадлежат Allium.

Роль Gherkin и CEL

Gherkin предоставляет обычные Feature, Background, Rule, Scenario, tags, DataTable и DocString. Готового import в нём нет. Технический schema import и lock digest живут в manifest. Строгие шаги .feature описывают Machine и Flow через смысловые порты, без имён RPC. Checker отклоняет неизвестный шаг.

CEL, прикреплённый как """cel DocString, в текущем профиле вычисляет типизированное содержимое target Input. Он не выбирает trigger и не исполняет следующий слой. Causation и права проверяются независимо от результата CEL. Текущий Machine profile сохраняет своё значение Feature; flow-v2 выбирается отдельным marker в .feature и в package manifest. Официальный @cucumber/gherkin AST уже подключён как отдельный syntax gate для пятнадцати compiler fixtures с явным profile selection. Он отвергает неподдержанные Rule, tags и DataTable; DocString допускается только для закрытого шага flow-cel-v1 с media type cel. Rust semantic compiler по-прежнему читает узкие строки и проверяет смысл шагов. Русские Gherkin keywords для этих исполняемых профилей ещё не включены. При сборке binding manifest Machine и Flow подаются в Rust как закрытый normalized AST через compile-ast и flow-compile-ast. Шесть Flow и пять Machine fixtures дают тот же IR byte-for-byte; два kernel fixtures сохраняют те же digests. Прямые старые команды сохраняют прежний source input.

Публикация

Локальный binding checker входит в pnpm quality и имеет positive/negative tests. Node собирает descriptor через Buf и вызывает Rust compiler; digest descriptor проверяется до компиляции binding, затем бинарный descriptor сверяется с результатом компиляции. Перед публикацией в Forgejo локально запускается pnpm quality; hosted CI и обязательный status check пока не настроены. pnpm check:capkin:federation теперь является единым локальным gate для действующих Type/Flow IR, CEL scalar-map, graph, publication, runtime replay и отрицательных fixtures. Он вызывается из pnpm quality. check:capkin:cross-language внутри него сверяет native Type, field-copy Flow v1, scoped Flow v2 и CEL Flow v1/v2 IR независимым Node reader официального Gherkin AST с Rust CLI. Неверные retry bound и CEL DocString type отвергаются обеими сторонами. Node также декодирует binary descriptor и сверяет TypeRef/PortBinding. Полный контракт CEL typing/cost и внешний gRPC transport conformance ещё остаются открытыми в OpenSpec change. Локальный protobuf receipt gate входит в check:capkin:federation. pnpm quality также выполняет strict OpenSpec validation текущего change.

Браузерное reference demo

Для bounded graph cycles Rust предоставляет compile_cel_condition_v1: закрытое CEL выражение над pinned scalar полями обязано возвращать bool, имеет отдельный engine ID и проверяемый digest, лимиты AST/depth/payload. Compile-only cycle preflight сверяет оба pinned Flow, exit для каждого source port и конечный budget; его нельзя открыть в LocalFlowGraphRuntime. Исполняемый opt-in tyapkin-local-flow-graph-release-v4 закрепляет оба binding IR/runtime schema, boolean exit для каждого source Output и конечный budget 1..16. enqueue_cycle допускает следующий переход только после committed target Input и нового Output/Trace, связанного с предыдущей delivery. При exit или budget новые delivery не создаются; SQLite terminal document и digest проверяются через verify_cycle_terminal. Повтор после restart узнаёт прежний delivery key и terminal digest. Это локальный однописательный host: Flow outbox и graph lineage пока не входят в одну SQLite transaction; распределённое исполнение требует отдельного admission/recovery контракта. Семь общих golden vectors для CelCondition независимо исполняются Rust и Node checker: true/false, ошибка non-bool, неизвестное поле, impure call и несовпадение типа source payload. Для scalar projection добавлены отказные vectors по пропущенному/неизвестному target полю и неверному типу результата. Проверка входит в общий federation gate.

Инспекция произвольного graph release

capkin flow-graph-inspect --file <release.json> --sha256 <digest> проверяет точные bytes release и каждый закреплённый binding IR/runtime schema, используя тот же Rust graph resolver, что и локальный runtime. Его versioned read model содержит Machine contracts, SemanticPort, Flow и projection. Это read-only инспекция деклараций: она не открывает Machine instance, не ставит Flow в outbox и не создаёт Trace.

При DEMO_GRAPH_RELEASE=<путь> общий Entity Explorer загружает выбранный release без fixture-specific loader. Кнопка «Показать маршруты release» открывает понятные переходы Machine → Output port → Flow → Input port → Machine. «Весь mesh» дополнительно показывает TypeRef, Projection и все отношения. Выбор узла сохраняет выбранный вид и показывает точный binding digest, origin и связи. Машины с одинаковым именем в разных release не склеиваются. Одно имя TypeRef может иметь разные protobuf descriptor origins в разных Flow; такие определения также остаются раздельными. Поля protobuf появляются только из отдельно проверенного Type index опубликованного package: binding IR сам по себе подтверждает origin, но не содержит структуру полей. Процедура и Playwright evidence описаны в docs/runbooks/capkin-federation-demo.md.

CARGO_HOME=/tmp/tyapkin-cargo pnpm demo:capkin:federation собирает локальный Rust runner и через Playwright показывает Asset Output → ServiceCase Input из закреплённого release-v5. Он записывает video, trace.zip, кадры, SQLite и manifest в новый каталог runs/demo/capkin-federation/<timestamp>/. Сквозной путь показывает два независимых Trace и federated read. Отдельная кнопка открывает strict graph release-v3: два Flow от ServiceCase Output дают независимые delivery keys, target Trace и replay после restart. Отдельный CEL v2 route строит Input.reason = maintenance из Output.case_id = case-1 по условному выражению Feature и проводит отдельный target Trace с pinned release и replay. Кнопка bounded cycle исполняет ServiceCase → Scheduling → ServiceCase → Scheduling: три отдельные delivery и четыре Trace, затем остановку по budget до четвёртой delivery; независимый root Output демонстрирует typed exit до первой delivery. Video/trace и SQLite counts проверяют оба terminal documents и replay после restart. Отдельный scoped вариант собирает два flow-v2 binding в один release-v4: оба направления проверяют tenant/object correlation и authority перед target Input, сохраняя те же три причинные delivery, четыре Trace и конечную остановку. Foreign tenant и другой object отвергаются до первой delivery. Отдельный checked protobuf package публикует оба flow-v2 Feature с собственными binding artifacts; publication сверяет authority, scope, retry и outcome policy с Gherkin и отвергает подмену. Package publication и graph runtime используют exact binding bytes. Типизированный эффект проверяется отдельным opt-in контрактом. Graph DENY показывает локальное terminal evidence без target Transition. Graph transport показывает lost response, lookup и локально проверенный receipt без target Transition; demo verifier тестовый и не доказывает подлинность внешнего адаптера. Остальные кнопки отказа вызывают forged Output, DENY, чужой tenant, stale read и неверный release pin. Подробная процедура — docs/runbooks/capkin-federation-demo.md.

Демо использует только fixture packages и локальный reference host. Его Output intention не является доказательством внешнего эффекта, а read snapshot не меняет CAP State.

Типизированный эффект v2

Строгий machine-kernel-v1 Feature может добавить строку And typed binding CaseOpened port ServiceCase.CaseOpened@1 payload ServiceCase.CaseOpenedPayload@1 result Scheduling.VisitScheduledPayload@1 descriptor_sha256 <digest>. Она наследует binding id/version/executor от уже объявленного Output и выдаёт отдельный digest TypedEffectBindingV2. Прежний binding digest, Machine IR и Trace остаются прежними. TypedEffectSchemaV2::open повторно проверяет exact binding IR pin, binary FileDescriptorSet, TypeRef index, направление и symbol Output port, payload/result messages. Текущий opt-in профиль принимает канонические protobuf bytes с заполненными ненулевыми scalar полями. prepare_from_host сначала требует verified OutputIntent, затем связывает его hash с payload bytes и номером попытки.

Checked protobuf package может включить typedEffects с exact Gherkin source artifact. ContractStore::publish повторно компилирует его, сверяет CAP IR с Machine declaration и TypeRef/port с package descriptor. typed_effect_schema открывает схему только по опубликованному ID/version/digest и Output port. Additive версия обязана сохранить source bytes прежнего typed effect. Локальный KernelHost::with_typed_effects закрепляет его digest при создании экземпляра; типизированный dispatch пишет outbox status и receipt v2, восстанавливает прерванный вызов как UNKNOWN и проверяет сохранённые receipts после restart. Некорректный успешный результат переводится в UNKNOWN, а не в ложный успех.

cap.v2.TypedEffectRequest и TypedEffectReceipt описаны в proto/cap/v2/typed_effect.proto. Core вычисляет domain-separated digests; receipt v2 связывает exact request, binding, executor, result TypeRef и hash результата. Для успешного результата schema adapter проверяет protobuf форму; внешний origin требует валидную attestation от настроенного authenticator. Локальный mock test не доказывает внешнее исполнение. Adapter активирует host после выбора точного package release и executor; автоматический вызов внешнего provider не заявлен. pnpm check:capkin:typed-effect и pnpm check:capkin:typed-effect-publication сверяют descriptor, package, независимые Node/Rust golden digests, replay и отрицательные случаи.

В локальном Playwright demo сценарий typed-effect публикует этот checked package, отклоняет поддельный payload и подтверждает один receipt после restart. Изолированная SQLite содержит четыре commitments, один binding pin и один typed receipt; video/trace и точные SHA-256 описаны в docs/runbooks/capkin-federation-demo.md. Это проверка локального executor, а не свидетельство вызова внешнего сервиса.

On this page