Subproject.java

package network.ike.workspace;

import java.util.List;

/**
 * A workspace subproject — one git repository in the workspace manifest.
 *
 * <p>The {@code state}, {@code tag}, and {@code kind} fields express the
 * subproject's <strong>alignment</strong> with the workspace's release
 * cascade (ike-issues#233). The four-state alignment model:
 *
 * <ul>
 *   <li><b>snapshot-aligned</b> — {@code state="snapshot"} (the default
 *       on legacy manifests). The subproject is in the workspace, tracked
 *       as a SNAPSHOT, and released by {@code ws:release}.</li>
 *   <li><b>tag-aligned</b> — {@code state="tag-aligned"} with
 *       {@code tag="<git-tag>"} and {@code kind="release"} or
 *       {@code kind="checkpoint"}. Source on disk for IDE debugging,
 *       but pinned at a tag and not in the release cascade.</li>
 *   <li><b>external-consumer</b> — absent from the manifest entirely;
 *       referenced by released GAV from Nexus.</li>
 *   <li><b>unrelated</b> — neither in the manifest nor referenced.</li>
 * </ul>
 *
 * <p>Goals introduced by #233 ({@code ws:attach-snapshot},
 * {@code ws:attach-release}, {@code ws:attach-checkpoint},
 * {@code ws:promote}, {@code ws:demote}, {@code ws:detach}) drive
 * transitions across the lattice; {@code ws:add} / {@code ws:remove}
 * remain shortcuts for external-consumer ⇄ snapshot-aligned.
 *
 * @param name         the subproject identifier (directory name and YAML key)
 * @param description  human-readable purpose
 * @param repo         git clone URL
 * @param branch       the branch to track
 * @param version      Maven version string, or null if not versioned
 * @param groupId      Maven groupId
 * @param dependsOn    inter-repository dependencies
 * @param notes        free-text migration or status notes
 * @param mavenVersion Maven version for the wrapper (e.g., "4.0.0-rc-5"),
 *                     overrides {@link Defaults#mavenVersion()}. Null to inherit.
 * @param parent       subproject name of the Maven parent POM, or null if the
 *                     parent is not a workspace subproject. Used by
 *                     ws:scaffold-draft (which folds verify per #393) and
 *                     ws:align-publish to enforce parent version alignment.
 * @param sha          git commit SHA to check out. When present, {@code ws:scaffold-init}
 *                     checks out this exact commit instead of branch HEAD.
 *                     Written by {@code ws:checkpoint-publish}. Null means use
 *                     branch HEAD.
 * @param state        alignment state — {@code "snapshot"} (default,
 *                     in release cascade) or {@code "tag-aligned"} (frozen
 *                     at a tag, not in release cascade). Schema 1.1+ field
 *                     (ike-issues#233); legacy manifests default to
 *                     {@code "snapshot"} on read.
 * @param tag          git tag this subproject is pinned to when
 *                     {@code state="tag-aligned"}; null when
 *                     {@code state="snapshot"}.
 * @param kind         when {@code state="tag-aligned"}, distinguishes
 *                     {@code "release"} (artifact is in Nexus) from
 *                     {@code "checkpoint"} (artifact may need local
 *                     {@code mvn install}). Null when
 *                     {@code state="snapshot"}.
 */
public record Subproject(
        String name,
        String description,
        String repo,
        String branch,
        String version,
        String groupId,
        List<Dependency> dependsOn,
        String notes,
        String mavenVersion,
        String parent,
        String sha,
        String state,
        String tag,
        String kind
) {

    /** Sentinel for the snapshot-aligned state — the default alignment. */
    public static final String STATE_SNAPSHOT = "snapshot";

    /** Sentinel for the tag-aligned state. */
    public static final String STATE_TAG_ALIGNED = "tag-aligned";

    /** Sentinel for the {@code release} sub-kind of tag-aligned. */
    public static final String KIND_RELEASE = "release";

    /** Sentinel for the {@code checkpoint} sub-kind of tag-aligned. */
    public static final String KIND_CHECKPOINT = "checkpoint";

    /**
     * Whether this subproject participates in the workspace's release
     * cascade — {@code true} for snapshot-aligned (the default),
     * {@code false} for tag-aligned. Convenience over comparing
     * {@link #state()} to a sentinel.
     *
     * @return true if {@link #state()} is {@code "snapshot"} or null
     *         (legacy manifest default)
     */
    public boolean isSnapshotAligned() {
        return state == null || STATE_SNAPSHOT.equals(state);
    }

    /**
     * Whether this subproject is pinned to a tag and excluded from the
     * release cascade.
     *
     * @return true if {@link #state()} equals {@code "tag-aligned"}
     */
    public boolean isTagAligned() {
        return STATE_TAG_ALIGNED.equals(state);
    }
}