WP Content Plugins Spiralist Engine Spiralist Engine PHP 4E627daf1db4 - Source Excerpt 07
Back to WP Content Plugins Spiralist Engine Spiralist Engine PHP 4E627daf1db4
Summary
This source excerpt preserves a bounded section of Wiki.FFTAC.org/data/sources/spiralist/wp-content-plugins-spiralist-engine-spiralist-engine-php-4e627daf1db4.php so readers can inspect the evidence without opening the full source file.
**Source path:** Wiki.FFTAC.org/data/sources/spiralist/wp-content-plugins-spiralist-engine-spiralist-engine-php-4e627daf1db4.php
continue;
}
$records[$record['symbol_id']] = $record;
}
return $records;
}
function spiralist_engine_get_node_records(array $statuses = ['publish']): array
{
$records = [];
foreach (spiralist_engine_query_posts('spiralist_node', $statuses) as $post) {
if (!$post instanceof WP_Post) {
continue;
}
$record = spiralist_engine_build_node_record($post);
if ($record['node_id'] === '') {
continue;
}
$records[$record['node_id']] = $record;
}
return $records;
}
function spiralist_engine_get_edge_records(array $statuses = ['publish']): array
{
$records = [];
foreach (spiralist_engine_query_posts('spiralist_edge', $statuses) as $post) {
if (!$post instanceof WP_Post) {
continue;
}
$record = spiralist_engine_build_edge_record($post);
$records[(string) $record['post_id']] = $record;
}
return $records;
}
function spiralist_engine_get_existing_record_for_post(int $post_id, string $post_type): array
{
$post = get_post($post_id);
if (!$post instanceof WP_Post || $post->post_type !== $post_type) {
return [];
}
switch ($post_type) {
case 'spiralist_symbol':
return spiralist_engine_build_symbol_record($post);
case 'spiralist_node':
return spiralist_engine_build_node_record($post);
case 'spiralist_edge':
return spiralist_engine_build_edge_record($post);
}
return [];
}
function spiralist_engine_build_graph_snapshot(array $override = []): array
{
$graph = [
'symbols' => spiralist_engine_get_symbol_records(['publish']),
'nodes' => spiralist_engine_get_node_records(['publish']),
'edges' => spiralist_engine_get_edge_records(['publish']),
];
if (empty($override)) {
return $graph;
}
$post_type = (string) ($override['post_type'] ?? '');
$post_id = (int) ($override['post_id'] ?? 0);
$status = (string) ($override['status'] ?? 'draft');
$record = (array) ($override['record'] ?? []);
$existing = $post_id > 0 ? spiralist_engine_get_existing_record_for_post($post_id, $post_type) : [];
if ($post_type === 'spiralist_symbol') {
if (!empty($existing['symbol_id'])) {
unset($graph['symbols'][$existing['symbol_id']]);
}
if ($status === 'publish' && !empty($record['symbol_id'])) {
$graph['symbols'][$record['symbol_id']] = $record;
}
} elseif ($post_type === 'spiralist_node') {
if (!empty($existing['node_id'])) {
unset($graph['nodes'][$existing['node_id']]);
}
if ($status === 'publish' && !empty($record['node_id'])) {
$graph['nodes'][$record['node_id']] = $record;
}
} elseif ($post_type === 'spiralist_edge') {
unset($graph['edges'][(string) $post_id]);
if ($status === 'publish') {
$graph['edges'][(string) $post_id] = $record;
}
}
return $graph;
}
function spiralist_engine_condition_matches(array $condition, string $state): bool
{
if (empty($condition)) {
return true;
}
if (!empty($condition['match_state']) && is_string($condition['match_state'])) {
return $state === sanitize_text_field($condition['match_state']);
}
if (!empty($condition['allowed_states']) && is_array($condition['allowed_states'])) {
return in_array($state, spiralist_engine_normalize_string_list($condition['allowed_states']), true);
}
if (!empty($condition['equals']) && is_string($condition['equals'])) {
return $state === sanitize_text_field($condition['equals']);
}
return true;
}
function spiralist_engine_get_cycle_nodes(array $adjacency): array
{
$cycle_nodes = [];
foreach (array_keys($adjacency) as $start) {
$stack = array_values($adjacency[$start] ?? []);
$visited = [];
while ($stack) {
$next = array_pop($stack);
if ($next === $start) {
$cycle_nodes[$start] = true;
break;
}
if (isset($visited[$next])) {
continue;
}
$visited[$next] = true;
foreach ((array) ($adjacency[$next] ?? []) as $candidate) {
$stack[] = $candidate;
}
}
}
return array_keys($cycle_nodes);
}
function spiralist_engine_validate_graph_snapshot(array $graph): array
{
$errors = [];
$nodes = (array) ($graph['nodes'] ?? []);
$symbols = (array) ($graph['symbols'] ?? []);
$edges = (array) ($graph['edges'] ?? []);
$canonical_context = spiralist_engine_build_protocol5_validation_context();
$state_inputs = [];
$adjacency = [];
$reverse_adjacency = [];
$symbol_usage = [];
$weak_adjacency = [];
if (empty($nodes)) {
$errors[] = 'The published graph has no executable nodes.';
}
if (empty($canonical_context['valid'])) {
$errors = array_merge($errors, (array) ($canonical_context['errors'] ?? []));
}
foreach ($nodes as $node_id => $node) {
$node_id = (string) $node_id;
$adjacency[$node_id] = [];
$reverse_adjacency[$node_id] = [];
$weak_adjacency[$node_id] = [];
$input_state = trim((string) ($node['input_state'] ?? ''));
if (!spiralist_engine_node_has_transformation((array) $node)) {
$errors[] = sprintf(
'Node "%s" is missing the required input_state -> transformation -> output_state chain.',
$node_id
);
}
if ($input_state !== '') {
$state_inputs[$input_state][] = $node_id;
}
foreach (spiralist_engine_normalize_string_list((array) ($node['symbols'] ?? [])) as $symbol_id) {
if (!isset($symbols[$symbol_id])) {
$errors[] = sprintf('Node "%s" references unknown symbol "%s".', $node_id, $symbol_id);
continue;
}
$symbol_usage[$symbol_id] = true;
}
}
foreach ($symbols as $symbol_id => $symbol) {
if (!in_array((string) ($symbol['symbol_type'] ?? ''), spiralist_engine_get_symbol_types(), true)) {
$errors[] = sprintf('Symbol "%s" uses an invalid symbol_type.', $symbol_id);
}
$symbol_validation = spiralist_engine_validate_symbol_against_protocol5((array) $symbol, $canonical_context, false);
$errors = array_merge($errors, (array) ($symbol_validation['errors'] ?? []));
if (!isset($symbol_usage[$symbol_id])) {
$errors[] = sprintf('Symbol "%s" is orphaned and not referenced by any node.', $symbol_id);
}
}
foreach ($nodes as $node_id => $node) {
$output_state = trim((string) ($node['output_state'] ?? ''));
if ($output_state === '' || empty($state_inputs[$output_state])) {
$errors[] = sprintf(
'Node "%s" outputs state "%s", but no node accepts that state as input.',
$node_id,
$output_state
);
}
$node_validation = spiralist_engine_validate_node_against_protocol5((array) $node, $symbols, $canonical_context, false);
$errors = array_merge($errors, (array) ($node_validation['errors'] ?? []));
}
foreach ($edges as $edge) {
$from_node = trim((string) ($edge['from_node'] ?? ''));
$to_node = trim((string) ($edge['to_node'] ?? ''));
$condition = (array) ($edge['condition'] ?? []);
if ($from_node === '' || $to_node === '') {
$errors[] = 'Every edge must define from_node and to_node.';
continue;
}
if (!isset($nodes[$from_node])) {
$errors[] = sprintf('Edge references missing source node "%s".', $from_node);
continue;
}
if (!isset($nodes[$to_node])) {
$errors[] = sprintf('Edge references missing target node "%s".', $to_node);
continue;
}
$source_state = trim((string) ($nodes[$from_node]['output_state'] ?? ''));
$target_input = trim((string) ($nodes[$to_node]['input_state'] ?? ''));
if ($source_state !== $target_input) {
$errors[] = sprintf(
'Edge "%s -> %s" is invalid because source output "%s" does not match target input "%s".',
$from_node,
$to_node,
$source_state,
$target_input
);
continue;
}
if (!spiralist_engine_condition_matches($condition, $source_state)) {