ArchitecturePipelineStep.java

package com.taxonomy.architecture.pipeline;

import com.taxonomy.shared.extension.ExtensionKind;
import com.taxonomy.shared.extension.TaxonomyExtension;

/**
 * Extension SPI for architecture-view pipeline steps.
 *
 * <p>Each implementation encapsulates one deterministic transformation of the
 * {@link ArchitectureViewContext}, such as anchor selection, relevance propagation,
 * or leaf enrichment.  Implementations are registered as Spring beans and
 * discovered automatically by {@link ArchitecturePipelineStepRegistry}.
 *
 * <h2>Step contracts</h2>
 * <ul>
 *   <li><b>Core invariants</b> — steps with {@code coreInvariant = true} in their
 *       descriptor enforce structural requirements of the pipeline (valid anchors,
 *       valid elements, node-count limits).  Their ordering and semantics must not
 *       be changed; replacing or disabling them will break the pipeline output.</li>
 *   <li><b>Safe extension points</b> — steps with {@code coreInvariant = false}
 *       may be augmented by adding a new implementation that declares
 *       an {@link #order()} value adjacent to the step it extends. Replacing a
 *       default step requires changing bean registration so only one implementation
 *       occupies each unique order/ID slot, and the replacement must still leave
 *       the context in a consistent state for subsequent steps.</li>
 * </ul>
 *
 * <h2>Default step sequence</h2>
 * <ol>
 *   <li>100 {@code anchor-selection} — <em>core invariant</em></li>
 *   <li>200 {@code relevance-propagation} — <em>core invariant</em></li>
 *   <li>300 {@code element-build} — <em>core invariant</em></li>
 *   <li>400 {@code leaf-enrichment} — safe extension point</li>
 *   <li>500 {@code relationship-build} — <em>core invariant</em></li>
 *   <li>600 {@code provisional-relation} — safe extension point</li>
 *   <li>700 {@code node-limit} — <em>core invariant</em></li>
 *   <li>800 {@code impact-relation} — safe extension point</li>
 *   <li>900 {@code scoring-trace} — safe extension point</li>
 *   <li>1000 {@code impact-selection} — safe extension point</li>
 * </ol>
 *
 * <p>Example:
 * <pre>{@code
 * @Service
 * public class MyCustomEnrichmentStep implements ArchitecturePipelineStep {
 *
 *     @Override
 *     public String id() { return "my-custom-enrichment"; }
 *
 *     @Override
 *     public int order() { return 450; }  // runs between leaf-enrichment (400) and relationship-build (500)
 *
 *     @Override
 *     public void apply(ArchitectureViewContext ctx) {
 *         // read and write ctx as needed
 *     }
 * }
 * }</pre>
 *
 * @see ArchitecturePipelineStepRegistry
 * @see ArchitecturePipelineStepDescriptor
 * @see ArchitectureViewPipeline
 */
public interface ArchitecturePipelineStep extends TaxonomyExtension {

    /**
     * Returns the stable, kebab-case identifier for this step (e.g. {@code "leaf-enrichment"}).
     * Must be unique across all registered steps.
     */
    String id();

    @Override
    default String displayName() {
        return id();
    }

    @Override
    default String description() {
        return "Architecture pipeline step: " + id();
    }

    @Override
    default ExtensionKind kind() {
        return ExtensionKind.ARCHITECTURE_PIPELINE_STEP;
    }

    /**
     * Returns the numeric order value. Steps are executed in ascending order.
     * Must be unique across all registered steps.
     */
    int order();

    /**
     * Returns {@code true} when this step is part of the default pipeline.
     * Defaults to {@code true}; override to {@code false} for opt-in steps.
     */
    default boolean enabledByDefault() {
        return true;
    }

    /**
     * Applies this pipeline step to the given context.
     * Implementations may read from and write to any field in {@code ctx}.
     */
    void apply(ArchitectureViewContext ctx);

    /**
     * Returns the serializable descriptor for this step, derived from the
     * other interface methods.  Override for custom descriptor values.
     */
    default ArchitecturePipelineStepDescriptor descriptor() {
        return new ArchitecturePipelineStepDescriptor(id(), order(), enabledByDefault(), false);
    }
}