# Domain model
## Release
```rust
enum ReleaseIntent {
Existing, // 当前 commit 必须已有唯一 remote annotated calendar tag
New, // 没有 tag 时选择下一条 YYYY.MM.DD.N
}
struct ReleaseCandidate {
repository: String,
commit: String,
tag: CalendarTag, // tag 同时就是 cross-language version
intent: ReleaseIntent,
tag_already_sealed: bool,
}
```
```text
# identity invariant
(repository, tag, commit) 唯一标识一个 release。
# version invariant
version == tag;不存在 RELEASE_TAG/version_file/bump commit。
# immutability invariant
remote annotated tag 的 peeled commit、artifact identity 与 bytes 不可修改。
```
GitHub Release 的 `target_commitish` 在 tag 已存在时不是 commit proof;`release-tool` 只使用
`git ls-remote` 的 direct/peeled refs 建立 `tag -> commit` invariant。
`Release.version`(calendar tag)与执行工具自身的 SemVer(例如 `release-tool 0.3.0`)是两个
不同概念:前者标识本次 repository release,后者用于 config capability compatibility 和审计。
`New` 表示“允许为当前 commit 创建 tag”,不表示“无条件生成新版本”。当前 commit 已有唯一
有效 tag 时,`New` 复用该 tag,因此 `publish --release` 可安全重跑。
## Target
```rust
trait TargetAdapter {
// read-only;把 ReleaseCandidate 解析为确定的 artifact identities
fn resolve(&self, release: &ReleaseCandidate) -> Result<TargetPlan>;
// build/package;输出与 TargetPlan 完全相同的 inventory 和 local digests
fn prepare(
&self,
plan: &TargetPlan,
dependencies: &PreparedDependencies,
staging: &Path,
) -> Result<ArtifactManifest>;
}
```
`Target` 回答“从这个 release 生成什么”,不决定 remote write policy。
同一个 `ReleasePlan` 中两个 selected targets 不得声明相同的 remote artifact identity;冲突在
Resolve 结束前失败,而不是靠发布顺序决定谁覆盖谁。
OCI tag 的 collision identity 是 `repository + tag`;platform 是被验证的 manifest 属性,不是
独立 namespace,因此两个 platform 也不能由不同 targets 写入同一个 tag。
```text
DockerArchiveTarget
input: image/build/local-check facts
output: <asset>.tar.xz + <asset>.tar.xz.sha256
MavenReactorTarget
input: root POM + selected reactor projects
output: derived Maven GAV jar/pom identities
OciImageTarget
input: one image config + prepared direct dependencies
output: one OciImage identity + immutable Existing/Reuse/Build metadata
```
Targets 通过 `depends_on` 形成 DAG。Resolve 展开选中 targets 的 transitive dependency closure;
Prepare、Publish、Verify 按稳定拓扑顺序执行。`PreparedDependencies` 让 downstream 看到 dependency
的 artifact/content identity,但不暴露 `Build / Reuse / Existing` execution history。v1 只支持
`oci_image -> oci_image` handoff。
`OciImageTarget` 隐藏 previous candidate discovery 和 decision:project-owned `reuse_check` 决定
exact candidate 可否复用,release-tool 不定义 adopter inputs 或 fingerprint schema。
## Publisher
```rust
trait Publisher {
fn inspect(&self, plan: &TargetPlan) -> Result<PublicationState>;
fn publish(&self, manifest: &ArtifactManifest) -> Result<PublicationReceipt>;
fn verify(&self, manifest: &ArtifactManifest) -> Result<VerificationReport>;
fn verify_existing(&self, plan: &TargetPlan) -> Result<VerificationReport>;
}
```
`Publisher` 回答“remote 当前是什么状态、能否恢复、如何写入和验证”。它不运行 build,
也不重新解析 version 或 artifact inventory。
```text
PublicationState
├── ABSENT # 所有 planned identities 都不存在
├── COMPLETE # identities 完整;仍需 Verify
├── PARTIAL # publication 未完成,或 target-owned validation 尚未运行
└── INVALID # metadata/content 与 release identity 冲突
```
OCI current tag 即使存在也先报告 `PARTIAL`,因为 Publisher 不能代替 Target 运行需要 prepared
dependencies 的 `reuse_check`;check 完成后才形成可 Verify 的 `Existing` manifest。
`OciRegistryPublisher` 一次处理一个 `oci_image` target:对 `Build` 执行 push,对 `Reuse` 执行
exact manifest remote-retag,`Existing` 只验证。它执行 write-before/write-after Inspect。
`oci_image` 在配置层隐式选择它,不需要 publisher table/reference。OCI registry 不提供通用
conditional tag create,因此 v1 仍要求 single writer 或 registry immutable-tag enforcement。
## ArtifactManifest 与 receipt
```rust
struct PreparedArtifact {
identity: ArtifactIdentity,
path: PathBuf,
sha256: String,
}
struct ArtifactManifest {
target: String,
publisher: String,
release: ReleaseCandidate,
artifacts: Vec<PreparedArtifact>,
}
```
发布前会重新计算每个 local artifact 的 SHA-256。文件在 `Prepare` 后发生变化时停止,不把
未记录的 bytes 写入 remote。
```text
PublicationReceipt
├── release-tool version
├── repository / tag / commit
├── target / publisher
├── artifact identities
└── skipped
```
receipt 记录审计 identity;`skipped = true` 只表示 remote write 被省略,不表示省略 Verify。
OCI prepared metadata 记录 destination identity、platform、config digest 与内部 action。Build
dependency 向 downstream 提供已 Inspect 的 local tag;Reuse/Existing dependency 提供
digest-pinned remote reference。Publish 只消费这份 metadata,不重新运行 builder 或 freshness check。