Status: C0-C4 are implemented for collective classification dependencies, including runtime-owned individual characterization; C5 live acceptance remains. This is a continuation of OBSERVATION.md, not a declaration that the staging example runs.
Classification resolves an abstract predicate X in a substantial Y. It attributes a concrete strict specialization Z of X to each selected Y observation. Observation identity, geometry, parent and cohort membership are preserved. The classification request is an operation, not a new observation or cohort. Instantiating missing Ys is a separate prerequisite which may create observations. Characterization then resolves Z of Y in the scope of that particular observation. Both lifecycle transitions belong to Runtime, not to user-authored continuations.
| Request | Activity | Member source | Successful result |
|---|---|---|---|
| Abstract X of each Y | CLASSIFICATION | Query existing cohort support; resolve missing collective support | Existing/new Ys acquire concrete Z; no classification-result observation |
| Abstract X of Y | CLASSIFICATION | Existing Ys in the contextual scope | Same semantic update, without implicitly demanding an instantiation |
| Concrete Z of Y | CHARACTERIZATION | The classified Y, used as its own contextual scope | Explanation of its attributed predicate; no replacement Y |
| Concrete Z of each Y | CHARACTERIZATION | Distribution across members | Per-member characterization, not classification merely because each occurs |
| Predicate of quality Q | TRANSFORMATION | The base quality graph | Existing transformation contract, outside this work |
Abstraction selects classification versus characterization. Collective inherence selects how
members are obtained. isInstantiation() historically includes collective predicate processing;
use the new modifiesExistingObservations() distinction when deciding whether an observation
result may be registered. Do not equate a successful empty cohort with missing coverage.
The sample is corrected, as confirmed by the maintainer:
model geography:Tanzania earth:Region
observing earth:PhysicalEnvironment of each earth:Region;
model earth:PhysicalEnvironment of each earth:Region
using klab.generators.random.categories();
private model klab:random:objects:polygons
as each earth:Region;
A classifier attached to the Tanzania model would contextualize the parent, not implement its dependency. The instantiator now directly produces the requested Region collective.
| Boundary | Current path and missing behavior |
|---|---|
| Semantics | Contextualization.forSemantics previously classified any collective inherent, even for a concrete predicate. It now distinguishes abstraction and rejects non-substantial/non-quality inherents. |
| Strategy selection | Activity patterns and ordinary producers already parse. Added a Tier-0 member-resolving classification strategy and direct characterization strategy. Lowering accepts the Tier-0 classification members plan and individual characterization. Classification requires an explicit resolved members input; unsupported forms are rejected. |
| Resolver | ResolutionCompiler branches to an internal OperationTarget before observation query/registration for semantic updates, both at roots and in model dependencies. The existing resolution API remains unchanged. Y uses ordinary collective query and missing-scale resolution. |
| Dataflow | Portable UPDATE actuators carry operation semantics, requested support and typed target bindings, with no result observation. CompiledDataflow dispatches CLASSIFICATION updates without allocating observations; other semantic updates remain rejected. |
| Invocation | The generic ContextualizerExecutor remains observation-oriented. C2 adds MemberClassifierExecutor, which invokes the selected local method with operation semantics and member context, validates returned concepts, and retains pending attributions. |
| Results | MemberClassifierExecutor returns pending attributions; TransactionImpl.stageAttributions builds detached semantic views and before/after audit records. The observation-oriented result scope is bypassed. |
| Completion | RuntimeService.submitContextualizationResult explicitly throws for CLASSIFICATION. Its instantiation branch submits created children and waits for them. Characterization requires a separate, optional-explanation outcome, not a blanket swallowing of execution failures. |
| Transactions | DigitalTwinImpl.TransactionImpl shares modified/added state with the root. C3 stages detached observations and publishes semantics only after durable root commit; child failure poisons the root. |
| Provenance | Child contextualization activities formerly wrote CONTEXTUALIZED for every outcome. They now choose typed links. C3 links CLASSIFIED to each affected member with before/after observable URNs, predicate family/result, support and event. |
- Added abstraction-aware predicate activity selection and a semantic-update activity predicate.
- Added typed effect relationships and changed the current transaction writer to use them: INSTANTIATED, ACKNOWLEDGED, DETECTED, SIMULATED, MEASURED, QUANTIFIED, VALUED, CATEGORIZED, VERIFIED, CLASSIFIED, CHARACTERIZED, TRANSFORMED and CONNECTED.
- Removed the generic CONTEXTUALIZATION activity type and CONTEXTUALIZED effect relationship. Execution activities use the actual contextualization type; orchestration types such as SUBMISSION and RESOLUTION remain. CONTEXTUALIZED_BY remains the observation-to-actuator relationship and has a different purpose. Old graph compatibility is not supported.
- Updated Neo4j query visibility to follow all activity effects. New effect edges are deliberately absent from deletion ownership. Classifying an existing observation does not make it owned by the classifying context. The old CONTEXTUALIZED deletion path is removed; no graph migration is provided.
- Added the two Tier-0 source strategies to the reference corpus and
imod/strategies/observations.obs. These are definitions awaiting the operation-target milestone, not runnable classifiers today. - Corrected the staging model separation. The resource and live numerical/classifier execution have not been exercised.
C0 validation: the eight-module Maven reactor compiled and 22 focused tests passed:
ClassificationContractTest (2), ObservationPipelineTest (5),
ObservationStrategyAdaptationTest (6) and Neo4jQueryCompilerTest (9). These verify semantic
dispatch, the expanded corpus and interface serialization, existing Tier-0 behavior, typed effect
visibility and the deletion boundary. They do not exercise classification against live members.
An execution activity's type is the corresponding Contextualization name. Activity.Type
is the canonical enum, also used by the GraphModel.Activity record and serialized as the
Neo4j Activity node's type property. There is no second, divergent graph-specific activity enum.
Activity.Type.forContextualization rejects null and VOID: neither describes executable work.
Submission, resolution and other orchestration activities retain their own types.
Activity node type |
Activity → affected Observation relationship |
|---|---|
| INSTANTIATION | INSTANTIATED |
| ACKNOWLEDGEMENT | ACKNOWLEDGED |
| DETECTION | DETECTED |
| SIMULATION | SIMULATED |
| MEASURE | MEASURED |
| QUANTIFICATION | QUANTIFIED |
| VALUATION | VALUED |
| CATEGORIZATION | CATEGORIZED |
| VERIFICATION | VERIFIED |
| CLASSIFICATION | CLASSIFIED |
| CHARACTERIZATION | CHARACTERIZED |
| TRANSFORMATION | TRANSFORMED |
| CONNECTION | CONNECTED |
CompiledDataflow creates the execution Activity using its target's contextualization.
DigitalTwinImpl.TransactionImpl creates the effect edge when that execution enters a child
transaction. It derives the edge from the activity type, not the target's current observable.
This matters when a classification activity changes an ordinary substantial observation.
flowchart LR
P[Parent Activity] -->|TRIGGERED| E[Activity: type MEASURE]
E -->|MEASURED| O[Target Observation: quality]
R[Activity owning the compiled plan] -->|HAS_PLAN| A[Root Actuator]
R -->|RESOLVED| O
O -->|CONTEXTUALIZED_BY| A
These edges belong to different parts of the graph:
| Edge | Source → destination | Writer and purpose |
|---|---|---|
| TRIGGERED | Parent Activity → child Activity | Transaction nesting; records causal execution hierarchy |
| Typed effect, e.g. MEASURED | Execution Activity → target Observation | Child contextualization transaction; records the operation's effect |
| CREATED | Submission/creation Activity → new Observation | TransactionImpl.setTarget; registration, not a replacement for an effect edge |
| HAS_PLAN | Activity owning the plan → root Actuator | CompiledDataflow; identifies the compiled plan |
| RESOLVED | Activity owning the plan → root Observation | CompiledDataflow; records resolution of the root |
| CONTEXTUALIZED_BY | Observation → its Actuator | CompiledDataflow; links the observation to its executable implementation, including geometry |
| HAS_CHILD / HAS_MEMBER | Parent Observation or Cohort → child/member Observation | Containment/membership; neither is a contextualization effect |
For current instantiation execution, the effect's target is the collective observation being contextualized. Newly produced individual observations have their own registration and subsequent acknowledgement activities. This change does not invent additional INSTANTIATED edges to every individual. In C1–C4, classification and characterization must instead execute against the actual members and link CLASSIFIED/CHARACTERIZED to each affected member; no directive observation is created. C3 implements CLASSIFIED member links; C4 implements CHARACTERIZED execution.
Neo4j visibility follows typed effects; context deletion does not use them as ownership evidence. The effect edge identifies the kind and target of work; the Activity outcome still determines success or failure. C3 records before/after attributed semantics for each classified member.
Typed-activity validation (2026-09-11): 18 tests passed across
ClassificationContractTest, DigitalTwinCommitTest, Neo4jQueryCompilerTest and
Neo4jQueryExecutionTest. Coverage includes every executable activity/effect pair, interface JSON
round trips, actual transaction edge direction and triggering links, and embedded Neo4j traversal
and context-deletion checks. No live service graph migration was performed or is supported.
Classification always consumes observed members. Resolving each Y is therefore a mandatory
prerequisite within the Tier-0 strategy, not a Tier-1 fallback. There is no classifier strategy
that bypasses member resolution. Existing complete cohort coverage makes that resolution a query
reuse; partial coverage triggers resolution of the missing support.
strategy 0 named classification.members
for pattern {
node {
activity = CLASSIFICATION;
inherent = capture members as node { collective = true; };
}
}
ensure context.exists()
resolve $members to cohort
observe $this with inputs(members = cohort);
strategy 0 named characterization.direct
for pattern { node { activity = CHARACTERIZATION; } }
ensure context.exists()
observe $this;
The classifier model still directly explains X of each Y; its strategy first obtains the members on which it operates. “No direct classifier strategy” does not remove the separate classifier model from the namespace. Classification dependency execution is enabled by C3; C4 adds characterization of each staged attribution.
An abstract classifier request must not use request.fully_specified to reject its deliberately
abstract predicate. Validate the inherent and operation signature instead. The members port
is compiled as a typed collective/member binding, not an ordinary scalar model argument. The current
classification strategy covers collective inherence; singular-inherent classification needs its
own existing-member binding contract before enabling that form.
The runtime must finish resolution of newly instantiated members before passing them to the classifier. Characterization is never written as a strategy continuation.
Implemented planning boundary. No endpoint or alternate request/result DTO was introduced.
Resolver.resolve(Observation, ContextScope) still returns Dataflow. Its observation-shaped
request may describe a directive, but the Resolver uses an unregistered probe only for the existing
Reasoner selection API; the actual graph vertex is an internal OperationTarget. Model dependencies
enter this path before requireObservation, and Runtime registration rejects directives.
| Portable actuator field | C1 meaning |
|---|---|
actuatorType = UPDATE, effect = SEMANTIC_UPDATE |
An operation affecting existing members; no result observation |
operationObservable, contextualization |
X-of-Y semantics and CLASSIFICATION/CHARACTERIZATION, independent of affected member semantics |
observation = null, id = 0 |
No allocated or transported directive observation; node identity is transientId |
requestedSupport, coverage |
Requested support and resolved plan coverage; neither records that classification has executed |
targetBindings: COHORT_MEMBERS |
Named child producers whose member sets must be combined after prerequisite execution |
targetBindings: OBSERVATION |
Existing individual context observation for characterization |
children |
Prerequisite computations/references, required before the update |
Classification lowers resolve $members and terminal observe $this with inputs(members = cohort).
The Resolver selects the classifier model in the requesting scope, retaining lexical constraints,
and attaches the resolved cohort as a prerequisite. Complete cached support is reused; partial support
requires an instantiator for the remainder. If scale subtraction cannot express that remainder, it
conservatively resolves full support instead of upgrading the cached portion to complete coverage.
Compiler-generated member source names are local to the update actuator and are not scalar arguments
passed to the classifier. The entire cohort output, including cached references, supplies members.
Actuator.TargetBinding is registered in JacksonConfiguration; implementations remain plain beans
without Jackson dependencies or annotations. Nested Dataflow transport preserves the operation and
bindings. SemanticUpdateTargets provides side-effect-free binding of completed producer member
sets, deduplicating positive durable IDs and transaction transient IDs in separate identity spaces.
A completed empty set is valid; an absent producer is an error. C2 uses this helper for supplied
completed member sets. C3 now enumerates completed cohort producers and stages their classifications;
C1 alone did not enumerate or classify live members.
At C1, the old blanket Reasoner lowerer guard was replaced by a Runtime whole-plan preflight guard. It ran
before requireObservations, storage preparation or executor construction, so a nested update cannot
partially execute as an observation-producing plan. C3 replaces the classification gate with explicit
operation dispatch and atomic effects; unsupported update kinds still fail preflight.
Individual characterization has a portable target; collective characterization and singular-inherent
classification remain unsupported pending their distribution/member-selection contracts.
The remaining C1 text records the accepted contract and acceptance criteria. The next implementation stage is now C5, validating the complete lifecycle against the live staging namespaces.
C1 validation: the eight-module offline Maven reactor passed 18 focused tests:
ClassificationPipelineTest (6), ObservationPipelineTest (5), DataflowCompilerTest (1),
ClassificationContractTest (2), and SemanticUpdateTargetsTest (4). These cover the actual parsed
reference corpus, Reasoner selection/lowering, strategy and nested Dataflow interface JSON transport,
cached/new/partial cohort planning, rejection of incomplete support, classification as a model
dependency, individual characterization, existing namespace/context propagation regressions,
deduplication, empty-versus-missing producer outputs, and pre-allocation rejection. External model,
query and runtime services are controlled doubles. No live worldview model lookup, cohort enumeration,
classifier execution, graph mutation, provenance extraction or replay was exercised. The test log is
target/c1-tests.log; git diff --check also passed.
Keep the existing resolution entry points and their Dataflow response. No new resolution or mutation endpoint is required. All runtime mutations, including classification and characterization, must occur through execution of a resolved Dataflow. Do not introduce a parallel request/result service contract for semantic updates.
Extend Dataflow and Actuator interfaces/beans as needed to describe the contextualization explicitly: operation observable, affected observation or member-graph binding, requested support, dependency ordering and result/effect kind. Distinguish a newly produced observation from an existing member whose semantics are changed. A semantic-update node must not register a result observation for X of Y. Resolver may use internal planning targets, but these are neither a public mutation API nor persisted observations. Preserve lexical namespace/project/scenario constraints at lookup.
Runtime dispatches from the contextualization declared in the resolved plan and validates the contextualizer signature against it. It must not infer the plan's meaning solely from Java return or parameter types. Classification outputs and completion states are internal execution/transaction results, not an alternative service response replacing Dataflow. Characterization follows the same resolution-to-Dataflow contract when Runtime schedules the mandatory follow-up.
Maintain interface-based transport: register any added abstract bean types in JacksonConfiguration, use plain portable fields with no Jackson annotations/dependencies, and test nested Dataflow/Actuator round trips. Do not serialize ContextScope, live executors or Resolver graph implementations.
This response is a contextual resolution Dataflow: it specifies work needed within the existing knowledge graph and can reference observations already present there. It is not a complete plan for rebuilding that graph. The latter is a separately extracted graph-reproduction dataflow, assembled from provenance with definitions and dependency closure sufficient to start from an empty graph. See the two contracts.
Implement classifier model selection and the Tier-0 members port, reusing existing query coverage and missing-scale calculation. Deduplicate members by durable or transaction-local identity. Cohort support and classification completion are separate quantities. Classifying only the cached members must not claim completion for an incompletely resolved requested cohort.
Acceptance: no new observation ID/CREATED link for X-of-Y; the member-resolving strategy remains rank zero; fully covered/partial/empty cohorts; existing and newly instantiated Ys; private model visibility; interface JSON round trips preserving contextualization and member bindings; no new resolution/mutation endpoints; all semantic changes occur through Dataflow execution. Planning tests permitted removal of the lowerer guard; C3 now supplies the classification execution stage.
Completed-stage prompt (retained for traceability): “Implement C1 in docs/CLASSIFICATION.md through the existing resolution API returning Dataflow. Extend its portable nodes to explicitly represent semantic-update contextualizations, Tier-0 member resolution and typed member bindings. Add no resolution/mutation endpoint, and ensure classification directives never become observations. Keep all changes within resolved Dataflow execution and preserve Jackson interface transport. Do not confuse this contextual plan with the separate provenance-extracted graph-reproduction dataflow. Update the running docs.”
Implemented pending-attribution boundary. MemberClassifierExecutor.compile selects one
unambiguous local implementation through ComponentRegistry; CompiledDataflow.compileMemberClassifier
exposes this stage for the runtime transaction integration. No endpoint was added. The normal
whole-plan execution path now consumes pending attributions through the C3 transaction stage.
The method must return Concept and accept exactly one Observable and one Scope/ContextScope.
It may additionally request one each of Observation, ServiceCall, Geometry and Scheduler.Event;
unknown, duplicate or ambiguous parameters and non-Concept return types are rejected. An instance
method requires a local receiver. The actual generator currently declares
Concept generateConcept(Observable, ServiceCall, Scope); the executor supports it without inventing
an Observation parameter. Every invocation receives the full X-of-Y operation observable, the
actual member when requested, and scope.within(member). Validation removes only INHERENT from
the operation to obtain the abstract X, preserving other restrictions.
The original model dependency is carried as nullable Actuator.modelDependency through interface
JSON transport. Only a non-null original dependency whose isOptional() is true permits the
classifier to return null. Root requests and strategy-only recursive requests do not acquire this
permission from their own optional flag. Missing implementations, incomplete cohort producers,
exceptions, and invalid concepts are never treated as optional empty results.
| Classifier outcome | Required dependency or other trigger | Optional original model dependency |
|---|---|---|
| Concrete, satisfiable strict specialization of X | Pending attribution | Pending attribution |
| Null (no concept, e.g. empty generator closure) | Failure | No attribution for that member |
| NOTHING / owl:Nothing | Inconsistency error | Inconsistency error |
| Abstract, equivalent to X, unrelated, unsatisfiable or non-predicate concept | Failure | Failure |
| Invocation exception | Failure | Failure |
The initial Concept-returning contract has no separate empty collection or Optional return wrapper. A successfully completed cohort with zero members succeeds independently of dependency optionality; it does not call the classifier. An unavailable producer is not an empty cohort.
Execution binds and deduplicates completed member sets. For a given compiled executor, member identity, event and support, concurrent/repeated calls share the same result or failure, so the contextualizer runs once. C3 creates a fresh invocation cache per root transaction attempt and compares persisted baselines under member locks to coordinate competing transactions. A batch publishes no successful list if any member fails. PendingAttribution records carry the member, original observable, abstract/concrete predicates, support and event; no observable, storage, ID, graph or provenance is changed.
C2 rejects any result for a member already bearing an X-family trait or role, including an identical attribution. It does not silently replace or accumulate classifications. C3 retains this conservative policy; it also rejects a second staged classification of the same member in one root attempt. Unrelated predicates and roles remain untouched.
C2 validation: the eight-module offline Maven reactor passed 25 tests, with none skipped:
MemberClassifierExecutorTest (8), SemanticUpdateTargetsTest (4), ClassificationPipelineTest
(8), and ObservationPipelineTest (5). The actual sibling generator source was compiled with the
Java compiler and invoked through its real method signature using a singleton closure for a
deterministic result and an empty closure for null-result behavior. Other tests cover typed member
arguments, invalid results including NOTHING for optional dependencies, failed batches, repeated
and concurrent invocation, reclassification rejection, registry selection, and optional dependency
provenance across JSON transport. Reasoner and service boundaries are controlled doubles; this does
not verify deployed component loading, live ontology closure, cohort enumeration or transaction
mutation. Log: target/c2-tests.log. To repeat the actual-source test, set
-Dclassifier.generator.source=<path-to-RandomContextualizers.java>; without it that one test skips.
Select a classifier executor from the operation contextualization. Resolve the contextualizer's signature before execution: operation Observable, member Observation and member ContextScope. Execute once per member per classified support/event, not once per grid cell or data shard. The operation observable stays X of Y while the Observation argument is the concrete member.
Require a concrete, consistent Concept Z of the appropriate predicate family and a strict semantic
specialization of X (is(Z, X) plus exclusion of semantic equivalence with X). A null result is
allowed only for an optional original model dependency after classifier selection and linking.
An abstract, unrelated, NOTHING or otherwise inconsistent result is always an error. Do not guess
category compatibility from URN prefixes. Restrict the first implementation to local concept-
returning contextualizers; report unsupported signatures explicitly.
Acceptance: actual klab.generators.random.categories signature; deterministically controlled
choices; mixed existing/new members; zero members with complete support; return validation; no
concurrent duplicate classification. Provide explicit policy/tests for reclassification before
silently replacing an existing X-family predicate. Preserve unrelated predicates and roles.
Completed-stage prompt: “Implement C2: a member classifier executor with typed invocation and semantic closure validation. Retain returned concepts as pending attributions; do not mutate persisted observations or create result observations. Test the actual generator signature and invalid returns.”
Implemented for the Tier-0 collective classification dependency. The existing resolution API still returns Dataflow. No resolution or mutation endpoint was added, and neither operation execution nor audit creates an observation for X-of-Y. Root directive submission, singular-inherent classification and characterization are not newly enabled by this milestone.
Execution proceeds as follows:
CompiledDataflowcompiles UPDATE nodes throughMemberClassifierExecutor, with separate actuator identities even though their result observation IDs are all zero. UPDATE children are explicit prerequisites of their consuming executor. A parent with an empty computation still registers its executor when it has an UPDATE dependency. No update receives an observation scheduler entry, storage allocation orAFFECTSendpoint.- Ordinary member producers execute through the scheduler in the current event. Instantiation's
existing completion path waits for the submitted members. Cached reference producers reuse their
completed support. Runtime enumerates durable and transaction-local
HAS_MEMBERlinks and limits membership to the producer/request support intersection. The typed binding deduplicates members; a completed empty cohort succeeds, while a missing producer/cohort fails. - Classifier invocation validates the full batch before staging. Only null results permitted by an optional original model dependency are omitted. Runtime creates a CLASSIFICATION Activity carrying its plan FlowChart before execution and nests it under the current transaction Activity.
TransactionImpl.stageAttributionsbuilds each replacement with the member's Reasoner builder, usingwithTraitorwithRole, and checks consistency of the resulting observable. Detached copies preserve observation identity, URN, geometry, parent/cohort links and unrelated predicates. Transaction asset/link views expose the replacement; original graph vertices and live objects retain their old semantics until durable commit. This is a semantic overlay, not a general transaction-isolation implementation for arbitrary observation fields or arbitrary Cypher queries.- The root stores new members with final semantics and updates existing members through
KnowledgeGraph.Transaction.updateSemantics. Neo4j locks each existing member and compares its stored observable against the recorded baseline. Existing members are locked in ID order. A stale or missing target fails the entire transaction. Only semantic properties are replaced, preserving unrelated persisted state; indexedobservable,semanticsandsemantictypevalues change together. - Successful durable commit publishes semantics to live member objects and includes existing IDs
in
Commit.modifiedAssets; new members remain inaddedObservationswith final semantics. A local semantic-cache generation invalidates graph and scope caches across context views. Normal commit delivery invalidates client assets and adjacency; no new notification API is used. Failed invocations, staging or storage publish no semantic replacements. Evenfail(null)poisons the shared root state. Storage failure is reported before resource closure, so closure cannot accidentally commit an earlier successful portion of a failed batch.
The provenance contract is parent Activity -TRIGGERED-> CLASSIFICATION Activity -CLASSIFIED-> member.
Each CLASSIFIED edge has before and after observable URNs, abstractPredicate, predicate,
encoded support, event key, and member transient/local identity. Its endpoint supplies the durable
identity after storage. These links remain outside deletion ownership. The same portable audit list
is available as Activity metadata Metadata.IM_ATTRIBUTIONS (im:attributions). Known FlowChart
and attribution metadata survive Activity persistence; operation actuators preserve their portable
UPDATE fields for audit. These contextual-plan snapshots are not provenance-extracted reproduction
Dataflows, and persisted scheduler restoration of composite plans is not added here.
A successful child activity means its batch was staged. The enclosing root commit determines whether those changes became durable; a later failure leaves no durable attribution/effect edge. Client consumers must not treat intermediate ActivityFinished as a commit notification.
Reclassification remains conservative: an existing X-family attribution is rejected, including an identical result; concurrent or repeated staging of the same member is also rejected. Replacing or combining classifications requires a separate policy change. C4 uses the staged member views when adding characterization before the root commit.
Represent pending attributions with member identity, old/new observable, abstract predicate, concrete result, coverage/event and activity. Construct new observables with the Reasoner builder; attach traits versus roles correctly. Preserve observation ID, URN, geometry, cohort and parent. Avoid mutating hash keys while observations are vertices in graphs or members of sets.
Apply replacements within the root transaction, mark existing members modified, and include new members in their creation records with final semantics. Update persistence indexes and invalidate client/query caches from commit payloads. Store before/after semantics in provenance so extraction can reconstruct what was classified; a bare CLASSIFIED edge is insufficient for replay. Rollback must restore both in-memory views and durable state. Runtime activities must link to all affected members with CLASSIFIED, without making those effects deletion-ownership edges.
Acceptance: commit.modified contains existing members; no replacement identities/cohorts; optional empty classifier results leave members unchanged; new members carry final semantics; rollback after a later-member failure; concurrent classifications; provenance queries and client graph updates; deletion cannot remove independently owned members.
C3 validation: the five-module offline Maven reactor passed 51 focused tests, none skipped:
ClassificationContractTest (2), ClientKnowledgeGraphTest (15), ClassificationExecutionTest (1),
ClassificationTransactionTest (6), ClassificationPersistenceTest (4), MemberClassifierExecutorTest (8),
SemanticUpdateTargetsTest (4), DigitalTwinCommitTest (4), Neo4jQueryExecutionTest (4),
ContextualizationDiagnosticsTest (1), and ResolutionDiagnosticsTest (2). Coverage includes an empty
parent computation with a classifier dependency, existing/new-member staging and commit, optional
empty results, later failure, concurrent baseline conflicts, before/after edge properties, UPDATE
persistence/transport, typed activity diagnostics, client refresh and deletion ownership boundaries.
The actual generator source signature was compiled and invoked. Persistence tests run production
transaction/Cypher logic against embedded Neo4j through a thin driver adapter: the application's
current Bolt/Netty dependency combination cannot start the harness Bolt connector. This does not
constitute live Bolt, deployed worldview/model lookup, full staging execution, AMQP or IDE validation.
Log: target/c3-tests.log. A follow-up run passed both ClassificationExecutionTest cases, including
an additional regression proving scope-cache reload after another context advances the semantic
revision (target/c3-cache-tests.log). git diff --check passed. C4 is implemented below; C5 retains
live acceptance.
Completed-stage prompt: “Implement C3: connect MemberClassifierExecutor pending attributions to atomic staged attribution updates and CLASSIFIED provenance, including before/after semantics, commit propagation, query/client invalidation and rollback tests.”
Implemented. After each valid classification, Runtime builds concrete Z of singular Y and resolves it in
scope.within(member). The abstract X must not leak into this request. Keep the classification
transaction open until its required child lifecycle work finishes, as for instantiation.
Distinguish no explanatory model from failed resolution infrastructure or failed contextualizer execution. No characterization model is a successful terminal lifecycle condition; it does not undo classification. An execution failure follows the normal failure/rollback policy. Do not reclassify on completion of characterization or resolve the whole member recursively in a loop. Record CHARACTERIZED for actual characterization work, with a distinct no-model outcome when only the lifecycle decision was made. Apply the same no-model distinction to acknowledged newly instantiated members rather than suppressing arbitrary failures.
Acceptance: exactly one characterization request per new attribution; correct concrete semantics and member scope; no-model success; execution failure propagation; no observation creation; multiple members/child completion order; explicit provenance of actual work.
CharacterizationLifecycle runs immediately after stageAttributions. It retrieves the detached
staged member, focuses a scope on that member, and submits an unregistered characterization
request through the existing Resolver API. The request uses the returned concrete predicate and
the original singular substantial semantics. It does not recursively submit the whole member.
All child work is awaited before classification can finish; the root transaction remains the only
durable commit. A child failure clears pending attributions through the existing C3 failure path.
The portable Dataflow.ResolutionOutcome distinguishes RESOLVED, NO_MODEL, and FAILED.
NO_MODEL carries no computation and is non-empty in the legacy execution sense. The Resolver
uses it for characterization after successful model discovery finds no candidate. For
ACKNOWLEDGEMENT, any error-free empty resolution preserves the substantial's existence, including
no applicable strategy or candidates that contribute no significant dataflow. Here NO_MODEL
means no executable explanation was selected, not necessarily that no candidates were found.
Characterization retains the stricter discovery requirement. Reported errors, exceptional service
responses and contextualizer execution failures are not suppressed. The outcome survives the existing
Jackson Dataflow interface mapping without annotations or new endpoints.
For NO_MODEL, the successful Resolution Activity records resolutionOutcome=NO_MODEL; there
is no CHARACTERIZED link because no characterization work ran. For actual work, the Runtime
compiles CHARACTERIZATION UPDATE roots, executes their dependencies, and invokes the local
characterizer in the member scope. A successful Characterization Activity links to that existing
member through CHARACTERIZED and carries its plan FlowChart. The directive never becomes an
observation. Ordinary dependency observations remain governed by their own resolved plans.
The first executor supports public local contextualizers returning void or primitive boolean
(false fails). Parameters may be Observable, Observation, ServiceCall, Geometry, Scheduler.Event,
and exactly one Scope or ContextScope; Observable is required and duplicate parameter types are
rejected. Observation/Concept-returning characterizers are rejected. A model with dependencies and
no local computation is also executable. Remote/adaptor characterization functions are not yet
supported by this typed executor and fail compilation explicitly.
Duplicate attributions within a lifecycle batch are suppressed by member/predicate identity. The classification and characterization executors cache success or failure per root transaction, event and support, so repeated scheduler visits do not repeat lifecycle work. Controlled tests cover member scopes, request identity, execution-before-child-commit ordering, no-model audit, failed resolution versus failed execution, typed invocation, duplicate visits, effect ordering, JSON transport and rollback of staged classifications. Live worldview, AMQP and the forthcoming characterization namespace remain C5 acceptance work.
C4 validation: the six-module offline reactor passed 28 focused tests, including the existing
embedded persistence/rollback suite (target/c4-final-tests.log). No live services were restarted
or staging observations submitted during this milestone. git diff --check passed.
Completed-stage prompt: “Implement C4: Runtime-owned characterization after classification, using an explicit no-model success outcome. Test per-member scopes, no duplicate lifecycle work, commit ordering and the distinction between missing explanation and failed execution.”
Run the corrected staging namespace with the installed worldview and generator. Verify the classifier model is selected, Regions are queried/instantiated as necessary, each result is a concrete PhysicalEnvironment specialization, no classification observation appears, and existing members appear as modified in the commit. This namespace must succeed without characterization models. Add the forthcoming characterization namespace and check its member-specific execution.
Test cohort partial coverage, zero members, repeat submissions, failure on one member, concurrent contexts, and provenance extraction/replay inputs. Update OBSERVATION.md, OBSERVABLES.md and the domain context pack only with runtime claims supported by this evidence.
Prompt: “Complete C5 using the staging classification namespaces. Record live model selection, member identity/coverage, commits and typed provenance; distinguish test doubles from actual Runtime, Resources and knowledge graph evidence. Close stages only when their acceptance passes.”
Accepted classification resolution graphs now travel as FlowChart metadata on the Dataflow and completed RESOLUTION Activity. Operation nodes expose contextualization and optional-dependency status; links expose member bindings and coverage. C3 additionally records per-member attribution audit data and typed effects during execution. See resolution diagnostics for the transport contract, validation and deferred ActivityCard rendering.