Target-pack contract
A target pack is a plugin that contributes one target id, source profile, optional surfaces, runtime references, target analysis, artifact planning, printing, and toolchain integration.
Shared outer shape
Target pack
├── descriptor and strict options
├── immutable starter-project descriptor
├── source profile and surfaces
├── compilation session
├── analysis and target facts
├── sealed target program
├── artifact planner
├── target AST
├── printer
└── native project/toolchain integration
C# and Rust retain this shape. Their inner algorithms differ:
- C# analysis closes expected target types, provider selections, conversions, object shapes, call signatures, and native project requirements.
- Rust analysis closes ownership, borrows, lifetimes, fallibility, layouts, generic requirements, provider operations, and Cargo requirements.
Those are different facts inside the same architectural phase, not different architectures.
Source layout
Both target repositories use the same outer source directories:
src/
├── index.ts public plugin entry
├── public/ target and provider SDK exports
├── descriptor/ plugin description and starter projects
├── compilation/ per-build session and composition
├── options/ strict target option contracts
├── source/ native source profile and virtual declarations
├── providers/ native metadata and packaged capabilities
├── target-model/ closed native semantic vocabulary
├── policy/ native selection rules
├── analysis/ classification and sealed target program
├── backend/
│ ├── compile.ts stage coordination
│ ├── target-ast/ native syntax nodes and structural operations
│ ├── artifact-model/ complete output-plan contracts
│ ├── planner/ syntax construction from sealed facts
│ └── emission/ materialize complete output plans
├── print/
│ ├── source/ native source printer
│ └── project/ native project printer
└── toolchain/ native build handoff
Within a layer, group a responsibility in one subtree. For example,
backend/planner/objects/object-literals/ owns object-literal planning;
the expression dispatcher calls it rather than owning a second implementation.
File names and inner algorithms can differ where the native responsibilities
differ. C# reflection member readers do not need Rust lifetime-reader twins.
The shared layer contract in
test/architecture/tooling/target-layer-contract.mjs defines allowed imports.
Each target classifies its files against that contract. A legal import graph
is necessary, but does not prove that an operation preserved its semantics.
For example, this call selects an ordinary imported native method:
return probe.is_some() === false;
Rust must emit !probe.is_some(), not invent an is_none() call. A native
Option presence test may use is_none(), but only when its exact Option
identity has already been proved and retained in the target AST. The printer
spells a selected operation; it does not recover identity from method names.
Starter boundary
An official target may expose one pure starter-project function from its plugin. Given an already-validated project name, it returns target options, build/start/check scripts, authored starter files, and declarative native toolchain checks. The generic creator validates the complete result before it writes anything into the requested destination.
The host does not choose source, native commands, SDK versions, assembly names, or crate names for a target. Starter creation does not run target analysis and does not create a second compilation path.
Planning boundary
Planning may query:
- immutable source syntax and source navigation;
- finalized TSTS evidence;
- the sealed target program;
- exact target provider relations;
- artifact dependency contracts.
Planning may not:
- re-enter the checker;
- infer semantic identity from names or emitted text;
- mutate target analysis;
- reopen provider discovery;
- fall back to runtime reflection or dynamic invocation.
Artifact revisions
Target artifacts expose public and implementation facets. If analysis later strengthens a public requirement, such as a Rust generic bound or a C# object shape adapter, every exact dependent is reconsidered. A failed revision may remain internally for rollback but cannot be emitted as successful output.
Toolchain ownership
The target may generate a native project or emit sources for a user-owned
project. Open-ended native settings remain in .csproj or Cargo.toml; the
generic host never grows target-specific configuration branches.
Proof alignment
pudding-csharp and rust-pudding describe their checks in
scripts/verify/scenarios.json. A shared scenario ID means the same declared
bounded contract, not identical project contents or proof strength. Native-only
and unpaired assertions
remain explicit. For example, both suites check calculator addition, while
an ASP.NET server and a Rust lifetime signature have separate contracts.
From pudding-csharp, inspect those declarations without compiling projects:
node scripts/verify-all.mjs --scenarios --peer ../rust-pudding
This checks project coverage, source anchors and declared pairs. It does not
run the assertions. The complete bash scripts/verify-all.sh gate writes
.tests/verify-*/scenarios.json alongside its report. That artifact records
source hashes and project execution results. A compiled library with no
executed caller remains compile-only, even when its Cargo or .NET build passes.