From 3bbfb8377fe6fb44212bb2a87d51c56206b88872 Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Mon, 22 Jun 2026 12:41:27 +0200 Subject: [PATCH] Add SKEEP proposal docs module --- CONTRIBUTING.md | 66 ++- README.md | 18 +- docs/antora.yml | 1 + .../ROOT/pages/contributing/index.adoc | 8 +- docs/modules/skeep/nav.adoc | 4 + .../pages/001-tensor-collection-literals.adoc | 390 ++++++++++++++++++ docs/modules/skeep/pages/index.adoc | 64 +++ 7 files changed, 539 insertions(+), 12 deletions(-) create mode 100644 docs/modules/skeep/nav.adoc create mode 100644 docs/modules/skeep/pages/001-tensor-collection-literals.adoc create mode 100644 docs/modules/skeep/pages/index.adoc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 304b8a47..1b3ffd86 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,65 @@ # Contribution Guide -This project is currently in the setup phase, and a detailed contribution guide is not available yet. +SKaiNET uses the Gitflow branching model described in +[GITFLOW.adoc](GITFLOW.adoc). Keep ordinary fixes small, focused, and easy to +review. -We plan to expand this guide in the near future and cover topics such as: +## When to Write an SKEEP -* Clean and maintainable code -* Contributions involving AI agents -* Project structure and best practices +SKEEP stands for SKaiNET Enhancement and Evolution Proposal. It is the +project's KEEP-style track for changes that need a durable design record before +or alongside implementation. -We already use the Gitflow branching model, as described here: [Gitflow branching model](GITFLOW.adoc) +Write an SKEEP when a change affects: -For now, don’t hesitate to contribute. Please use common sense, aim to be helpful, and keep your contributions clear and well-structured. If you’re unsure about something, simple and practical improvements are always welcome. +* public Kotlin APIs; +* DSL syntax or semantics; +* tensor dtype, shape, storage, or execution behavior; +* compiler, graph export, or runtime integration; +* compatibility or migration policy; +* documentation structure for a long-lived feature area. -Thank you for helping shape the project in its early stages 🙌 +You usually do not need an SKEEP for local bug fixes, internal refactors, +dependency bumps, test-only changes, typo fixes, or implementation details that +do not affect user-visible behavior. + +## SKEEP Procedure + +1. Create a feature branch. Prefer names like + `feature/skeep-001-tensor-collection-literals` or a similarly focused + feature name. +2. Pick the next available three-digit number under + `docs/modules/skeep/pages/`. Do not reuse numbers, even if a proposal is + later rejected or superseded. +3. Create the proposal as + `docs/modules/skeep/pages/NNN-short-title.adoc`. +4. Add the proposal to `docs/modules/skeep/nav.adoc`. +5. Add the proposal to the "Current Proposals" table in + `docs/modules/skeep/pages/index.adoc`. +6. Start new proposals with `Status: Draft`. Use `Accepted`, `Implemented`, + `Superseded`, or `Rejected` only when maintainers have made that decision. +7. Include the standard sections: summary, motivation, proposed design, + compatibility and migration notes, rollout plan, acceptance criteria, risks, + open questions, and references. +8. If the proposal depends on external language or platform features, link the + relevant upstream documents and call out stability or compiler-flag + requirements. +9. Keep implementation PRs connected to the SKEEP. The proposal explains the + shape of the decision; code changes prove and ship it. + +SKEEP files are part of the Antora docs component. The module is registered in +`docs/antora.yml` and lives separately from the normal contributor docs so that +design proposals can evolve as their own track. + +## General Contribution Expectations + +We plan to expand this guide over time. For now: + +* keep changes clear and well-scoped; +* prefer project-local patterns over new abstractions; +* add tests when behavior changes; +* update docs when user-facing behavior changes; +* keep generated or unrelated churn out of focused PRs. + +If you are unsure whether a change needs an SKEEP, open a small draft proposal +or ask in the issue or PR before implementing the whole feature. diff --git a/README.md b/README.md index 39ac917a..68b3ddf0 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,10 @@ val d = c.relu() val source = JvmRandomAccessSource.open("model.gguf") StreamingGGUFReader.open(source).use { reader -> println("Tensors: ${reader.tensorCount}") - + // Load specific tensor on demand (no whole-file loading) val bytes = reader.loadTensor("token_embd.weight") - + // Or get a TensorStorage descriptor with encoding/placement metadata val storage = reader.loadTensorStorage("token_embd.weight") } @@ -106,6 +106,20 @@ SKaiNET is a modular ecosystem. While this repository contains the core engine, | Examples and sample projects | [SKaiNET-examples](https://github.com/SKaiNET-developers/SKaiNET-examples) | | Interactive notebooks | [SKaiNET-notebook](https://github.com/SKaiNET-developers/SKaiNET-notebook) | | Eager backends & kernels (what runs where) | [Backends & kernels mindmap](docs/eager-execution-backends-and-kernels.md) | +| Design proposals and long-lived API decisions | [SKEEP proposals](docs/modules/skeep/pages/index.adoc) | + +--- + +## Contributing and Design Proposals + +Small fixes can go straight through the normal contribution flow described in +[CONTRIBUTING.md](CONTRIBUTING.md) and [GITFLOW.adoc](GITFLOW.adoc). + +Use a SKEEP when a change affects public APIs, DSL syntax, tensor semantics, +compiler/runtime integration, storage behavior, compatibility policy, or other +decisions that need a durable design record. SKEEP files live under +`docs/modules/skeep/pages/` and use three-digit numbering, starting with +`001`. --- diff --git a/docs/antora.yml b/docs/antora.yml index 947dcb18..674c3535 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -3,6 +3,7 @@ title: SKaiNET version: ~ nav: - modules/ROOT/nav.adoc + - modules/skeep/nav.adoc # Component-level attributes flow to every page. Defined here so the # operator-design article (and any future page) can reference them diff --git a/docs/modules/ROOT/pages/contributing/index.adoc b/docs/modules/ROOT/pages/contributing/index.adoc index 5d1d3fbb..dfd71f99 100644 --- a/docs/modules/ROOT/pages/contributing/index.adoc +++ b/docs/modules/ROOT/pages/contributing/index.adoc @@ -22,6 +22,8 @@ The Contributing section is for the engineer who: - Adds or replaces kernels in the CPU backend (scalar, Panama Vector, the planned native FFM provider). - Operates the self-hosted runner that publishes benchmark results. +- Drafts or reviews durable API and architecture proposals in the + xref:skeep:index.adoc[SKEEP proposal track]. Concretely: @@ -32,6 +34,7 @@ Concretely: | xref:contributing/benchmarks.adoc[Engine benchmark program] | The Phoronix Test Suite / OpenBenchmarking publication path: methodology, manifest, lanes, CI workflow, replay. | xref:contributing/matmul-kernels.adoc[Reading the matmul benchmark] | What the published numbers mean — scalar vs. Panama vs. quantized regimes, roofline reasoning, and a checklist for interpreting any new measurement. | xref:contributing/register-bench-runner.adoc[Register a self-hosted bench runner] | The one-time operator setup that lights up the full-publish CI lane on a Linux x86 box. +| xref:skeep:index.adoc[SKEEP proposal track] | How SKaiNET records long-lived API, DSL, runtime, compiler, storage, and compatibility proposals. |=== == What this section deliberately does *not* cover @@ -42,8 +45,9 @@ Concretely: - **What an operator does or how the public DSL works.** See xref:reference/operators/generated/index.adoc[Operator reference] and the Explanation pages. -- **Issue reports, branch policy, or commit conventions.** Those live - in repo-root files: `CONTRIBUTING.md`, `GITFLOW.adoc`, +- **Issue reports, branch policy, commit conventions, or the detailed + SKEEP authoring procedure.** Those live in repo-root files: + `CONTRIBUTING.md`, `GITFLOW.adoc`, `CHANGELOG.md`, `FAQ.md`. The published docs site intentionally does not duplicate them; they belong with the code. diff --git a/docs/modules/skeep/nav.adoc b/docs/modules/skeep/nav.adoc new file mode 100644 index 00000000..941c623e --- /dev/null +++ b/docs/modules/skeep/nav.adoc @@ -0,0 +1,4 @@ +.SKEEP +* xref:skeep:index.adoc[About SKEEP] +* Proposals +** xref:skeep:001-tensor-collection-literals.adoc[SKEEP-001: Tensor collection literals] diff --git a/docs/modules/skeep/pages/001-tensor-collection-literals.adoc b/docs/modules/skeep/pages/001-tensor-collection-literals.adoc new file mode 100644 index 00000000..c8c54d37 --- /dev/null +++ b/docs/modules/skeep/pages/001-tensor-collection-literals.adoc @@ -0,0 +1,390 @@ += SKEEP-001: Tensor collection literals +:description: SKaiNET proposal for using Kotlin collection literals and explicit context arguments to construct tensors. + +Status: Draft + +Audience: SKaiNET maintainers and contributors + +Created: 2026-06-22 + +== Summary + +Kotlin 2.4.0 introduces experimental collection literals using square brackets and allows custom types to participate through `operator fun of`. Kotlin 2.4.0 also introduces experimental explicit context arguments for context parameters. + +SKaiNET should use these features to provide concise tensor literals for examples, tests, teaching material, and small parameter fixtures while preserving SKaiNET's current execution model: tensors still materialize through an `ExecutionContext`, a `TensorDataFactory`, and an explicit dtype. + +The proposed design is intentionally two-step: + +1. Collection literals create lightweight literal values such as `VectorLiteral`, `MatrixLiteral`, or `TensorLiteral`. +2. SKaiNET materializes those literal values into `Tensor` through a function that receives an explicit or implicit tensor build context. + +[source,kotlin] +---- +val cpuFp32 = TensorBuildContext( + executionContext = DirectCpuExecutionContext.create(), + dtype = FP32::class, +) + +val weights = matrixOf( + tensorBuild = cpuFp32, + values = [ + [1f, 2f, 3f], + [4f, 5f, 6f], + ], +) +---- + +This avoids a direct `val weights: Tensor = [[...]]` design. Direct tensor literals look attractive, but they cannot cleanly bind SKaiNET's required execution context and dtype inside the collection-literal `of` operator. + +== Motivation + +SKaiNET's current tensor construction DSL is explicit and type-aware, but it is verbose for small dense tensors: + +[source,kotlin] +---- +val t = data(ctx) { + tensor { shape(2, 2) { from(1f, 2f, 3f, 4f) } } +} +---- + +This is acceptable for production tensor creation, but it creates friction in: + +* documentation snippets; +* unit tests and golden fixtures; +* tutorials for users coming from Python, Swift, JavaScript, or mathematical notation; +* model examples that need readable small matrices; +* code review, where shape and data are split across `shape(...)` and flattened `from(...)`. + +The main product issue is readability, not raw capability. SKaiNET can already build the tensors; it does not yet let users read small tensor fixtures in the same shape they represent. + +== Kotlin Feature Analysis + +=== Collection literals + +Collection literals are experimental in Kotlin 2.4.0 and require `-Xcollection-literals`. The compiler can translate bracket syntax to a type's static `operator fun of` when the expected type is known. Nested literals are resolved recursively, so matrix-like APIs can be modeled with row literal types. + +Relevant constraints for SKaiNET: + +* A custom literal target needs an `operator fun of` in the static scope of the expected type. +* The main `of` overload must expose a single `vararg` element path that the compiler can use for overload resolution. +* The `of` overloads cannot use extension receivers, context parameters, or context receivers. +* If no expected custom type is available, a literal falls back to Kotlin collection types, not SKaiNET tensors. + +These rules make `Tensor.Companion.of(...)` a poor materialization point. A real tensor needs context, dtype, storage strategy, and backend operations; collection-literal `of` cannot accept those as context parameters. + +=== Explicit context arguments + +Explicit context arguments are experimental in Kotlin 2.4.0 and require `-Xexplicit-context-arguments`. They let callers bind a named context parameter directly at the call site, and those explicit arguments participate in overload resolution. + +For SKaiNET, this is useful because tensor creation has two modes: + +* scoped use, where one tensor build context should be reused across several literals; +* one-off use, where a call site should state the context without opening a nested `data { ... }` block. + +[source,kotlin] +---- +context(tensorBuild: TensorBuildContext) +fun matrixOf(values: MatrixLiteral): Tensor + +val oneOff = matrixOf( + tensorBuild = cpuFp32, + values = [[1f, 2f], [3f, 4f]], +) + +context(cpuFp32) { + val a = matrixOf([[1f, 2f], [3f, 4f]]) + val b = vectorOf([1f, 1f]) +} +---- + +This addresses the current standalone DSL awkwardness documented in xref:ROOT:how-to/build-tensors.adoc[Build tensors with the data DSL], where the standalone path requires `tensor(ctx, dtype) { tensor { shape(...) { ... } } }`. + +== Goals + +* Allow small vectors and matrices to be expressed with Kotlin bracket literals. +* Preserve explicit SKaiNET context and dtype semantics. +* Keep the feature Kotlin Multiplatform-compatible in `commonMain`. +* Provide a migration path from today's `data { tensor { shape(...) { from(...) } } }` form. +* Make shape validation immediate and clear, especially for ragged matrix literals. +* Keep large tensors, model weights, and file-backed data on existing array, loader, and I/O APIs. + +== Non-Goals + +* Do not introduce Python, NumPy, PyTorch, or notebook-oriented workflows. +* Do not add a compiler plugin. +* Do not make `Tensor` materialize directly from `[[...]]` without an execution context. +* Do not infer dtype from numeric literals when it would hide SKaiNET's `DType` model. +* Do not optimize this path for large model weights. +* Do not replace the current `data { ... }` DSL. + +== Proposed Design + +=== Literal model + +Add lightweight literal carrier types in `sk.ainet.lang.tensor.dsl` or a new `sk.ainet.lang.tensor.literal` package. + +[source,kotlin] +---- +public sealed interface TensorLiteral { + public val shape: Shape + public val values: List +} + +public class VectorLiteral private constructor( + override val values: List, +) : TensorLiteral { + override val shape: Shape = Shape(values.size) + + public companion object { + public operator fun of(vararg values: V): VectorLiteral + } +} + +public class TensorRow private constructor( + public val values: List, +) { + public companion object { + public operator fun of(vararg values: V): TensorRow + } +} + +public class MatrixLiteral private constructor( + public val rows: List>, +) : TensorLiteral { + override val shape: Shape = Shape(rows.size, rows.firstOrNull()?.values?.size ?: 0) + override val values: List = rows.flatMap { it.values } + + public companion object { + public operator fun of(vararg rows: TensorRow): MatrixLiteral + } +} +---- + +The exact implementation can use immutable arrays or internal primitive arrays later, but the public behavior should stay value-like: a literal has a shape and a row-major value sequence. + +=== Tensor build context + +Add a named context value that carries the pieces needed to materialize a SKaiNET tensor. + +[source,kotlin] +---- +public data class TensorBuildContext( + public val executionContext: ExecutionContext, + public val dtype: KClass, +) +---- + +The context parameter name must be stable and public because explicit context arguments use the parameter name. Use `tensorBuild` consistently. + +=== Materialization functions + +Add materialization functions with context parameters. + +[source,kotlin] +---- +context(tensorBuild: TensorBuildContext) +public fun vectorOf(values: VectorLiteral): Tensor + +context(tensorBuild: TensorBuildContext) +public fun matrixOf(values: MatrixLiteral): Tensor + +context(tensorBuild: TensorBuildContext) +public fun tensorOf(values: TensorLiteral): Tensor +---- + +Materialization should call the existing `ExecutionContext` methods: + +* rank 1 `VectorLiteral` -> `fromFloatArray(shape, dtype, data)` +* rank 1 `VectorLiteral` -> `fromIntArray(shape, dtype, data)` +* rank 2 `MatrixLiteral` -> `fromFloatArray(shape, dtype, rowMajorData)` +* rank 2 `MatrixLiteral` -> `fromIntArray(shape, dtype, rowMajorData)` +* unsupported value storage -> `tensorDataFactory.init(shape, dtype) { ... }` + +The API should prefer typed failures over coercion. For example, `matrixOf([[1.0, 2.0]])` should not silently coerce `Double` to `Float`. + +=== Existing DSL interop + +Add convenience helpers from existing data scopes so users can write literals inside today's `data` blocks without manually creating `TensorBuildContext`. + +[source,kotlin] +---- +public fun TypedDataContextDsl.matrixOf( + values: MatrixLiteral, +): Tensor + +public inline fun DataContextDsl.matrixOf( + values: MatrixLiteral, +): Tensor +---- + +Example: + +[source,kotlin] +---- +val t = data(ctx) { + matrixOf([ + [1f, 2f], + [3f, 4f], + ]) +} +---- + +== Requirements + +=== Functional + +F1. Users can construct rank-1 tensors from bracket literals. + +[source,kotlin] +---- +val v = vectorOf( + tensorBuild = cpuFp32, + values = [1f, 2f, 3f], +) +---- + +F2. Users can construct rank-2 tensors from nested bracket literals. + +[source,kotlin] +---- +val m = matrixOf( + tensorBuild = cpuFp32, + values = [[1f, 2f], [3f, 4f]], +) +---- + +F3. Matrix literals reject ragged rows with a message that includes row index, expected width, and actual width. + +F4. Literal materialization preserves row-major ordering used by the existing `shape(...) { from(...) }` DSL. + +F5. Literal materialization uses the provided `ExecutionContext`, not a hidden global default. + +F6. Literal APIs are available from `commonMain`. + +F7. The old DSL remains source-compatible. + +F8. The experimental APIs are opt-in annotated until Kotlin collection literals and explicit context arguments are stable. + +=== Non-Functional + +N1. The feature must not add dependencies. + +N2. Common tests must cover JVM, JS, and native-compatible code paths where the module already supports them. + +N3. Literal materialization should avoid extra copies when the compiler and value type make that practical, but correctness and clarity are more important for the MVP. + +N4. Error messages must be deterministic and independent of platform. + +N5. Documentation must make clear that literals are for small tensors and fixtures, not large weights. + +== Compatibility and Migration + +This proposal is additive. + +Existing `data { ... }`, `createDataMap { ... }`, `tensor { shape(...) { ... } }`, and `ExecutionContext.from*Array(...)` APIs remain valid. The literal API should be documented as a compact fixture and tutorial path, not as the replacement for production tensor construction. + +The feature should be guarded by a SKaiNET experimental annotation until the Kotlin features are stable. Users who do not enable Kotlin 2.4 collection-literal flags can still use explicit carrier calls such as `MatrixLiteral.of(TensorRow.of(1f, 2f), TensorRow.of(3f, 4f))`. + +== Rollout Plan + +=== Phase 0: Design spike + +* Upgrade a local branch or sample module to Kotlin 2.4.0. +* Enable `-Xcollection-literals` and `-Xexplicit-context-arguments` only for the spike. +* Verify expected-type behavior for nested `MatrixLiteral` and `TensorRow`. +* Verify explicit context argument syntax with named `tensorBuild`. + +Exit criteria: + +* A common test compiles for vector and matrix literals. +* A one-off explicit context call and a scoped `context(cpuFp32) { ... }` call both compile. + +=== Phase 1: Literal carrier types + +* Add `VectorLiteral`, `TensorRow`, and `MatrixLiteral`. +* Add tests using explicit `VectorLiteral.of(...)` and `MatrixLiteral.of(...)` calls so most behavior is testable before the project-wide Kotlin upgrade. +* Add ragged-row validation. + +Exit criteria: + +* Shape and row-major flattening are covered by common tests. +* No Kotlin 2.4-only syntax is required for the core value model. + +=== Phase 2: Experimental materialization API + +* Add `TensorBuildContext`. +* Add `vectorOf`, `matrixOf`, and `tensorOf` materializers. +* Add `DataContextDsl` and `TypedDataContextDsl` convenience extensions. +* Gate APIs with a SKaiNET experimental annotation. + +Exit criteria: + +* `FP32/Float` and integer tensor tests pass. +* Existing tensor DSL tests remain unchanged. +* The new APIs do not alter `Tensor`, `TensorData`, or `TensorOps` contracts. + +=== Phase 3: Kotlin 2.4 bracket literal samples + +* Raise Kotlin from `2.3.21` to a compatible `2.4.x` version in a branch. +* Enable the experimental compiler flags in a sample or test source set first. +* Add docs examples using bracket syntax. + +Exit criteria: + +* Samples compile with Kotlin 2.4 experimental flags. +* Documentation includes the feature flags and the fallback syntax for users not enabling them. + +=== Phase 4: Promote or hold + +Promote after: + +* Kotlin feature stability and IDE support are acceptable for SKaiNET's release policy. +* The API has survived at least one release cycle as experimental. +* The implementation does not create surprising ambiguity with existing `vector`, `matrix`, or `tensor` DSL functions. + +Hold if: + +* Kotlin changes the `operator fun of` restrictions. +* IDE support makes the syntax hard to use in practice. +* Multiplatform compilation diverges across targets. + +== Acceptance Criteria + +* A user can create a `Tensor` from `[1f, 2f, 3f]` with one materialization call. +* A user can create a `Tensor` from `[[1f, 2f], [3f, 4f]]` with one materialization call. +* A ragged literal such as `[[1f], [2f, 3f]]` fails before tensor data is allocated. +* Examples show both explicit one-off context binding and scoped context binding. +* The feature works without changing existing `data { ... }` users. +* Docs state that Kotlin 2.4 experimental flags are required for bracket syntax. +* Tests prove equivalence with `shape(...) { from(...) }` for shape, dtype, rank, volume, and values. + +== Risks + +Direct `Tensor` literals are misleading:: +`Tensor` materialization depends on context, dtype, storage, and ops. Hiding those behind `Tensor.Companion.of` would make examples pretty but semantically weak. + +Expected-type inference is fragile:: +Bracket literals need an expected type. APIs should put literals in regular parameter positions like `values: MatrixLiteral` instead of requiring users to annotate local variables. + +Overload ambiguity can leak into user code:: +Avoid overloading `matrixOf` across too many literal carrier types. Prefer distinct names for vector, matrix, and rank-N tensor paths. + +Performance may disappoint for large tensors:: +Collection literals use varargs and small-object construction. This is fine for fixtures and tutorials, not large data. + +Kotlin feature instability:: +Both collection literals and explicit context arguments are experimental. SKaiNET should ship this behind its own experimental annotation and avoid promising source stability until Kotlin does. + +== Open Questions + +* Should rank-N support be included in the MVP, or should MVP stop at vector and matrix? +* Should literal carrier types expose `List`, primitive arrays, or only read-only indexed access? +* Should integer literals support `Int8`/`Int32` dtype aliases directly or require separate value type APIs? +* Should `TensorBuildContext` include memory placement or storage encoding when the backend grows beyond dense CPU defaults? +* Should docs prefer `matrixOf(values = [[...]])` or positional `matrixOf([[...]])` once IDE support is mature? + +== References + +* Kotlin KEEP-0416: Collection Literals, https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0416-collection-literals.md +* Kotlin KEEP-0448: Explicit context arguments, https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0448-explicit-context-arguments.md +* Kotlin 2.4.0 release notes, https://kotlinlang.org/docs/whatsnew24.html +* Existing SKaiNET tensor construction guide, xref:ROOT:how-to/build-tensors.adoc[Build tensors with the data DSL] +* Existing SKaiNET dtype guide, xref:ROOT:contributing/dtype-model.adoc[The SKaiNET dtype model] diff --git a/docs/modules/skeep/pages/index.adoc b/docs/modules/skeep/pages/index.adoc new file mode 100644 index 00000000..b8c55bd3 --- /dev/null +++ b/docs/modules/skeep/pages/index.adoc @@ -0,0 +1,64 @@ += SKEEP: SKaiNET Enhancement and Evolution Proposals +:description: SKaiNET's KEEP-style proposal track for language-facing APIs, public architecture decisions, and long-lived design records. + +SKEEP is SKaiNET's proposal track for design changes that need more durability than an issue or pull request description. + +It is inspired by Kotlin KEEP, but scoped to SKaiNET. A SKEEP can describe public APIs, DSL changes, compiler or runtime integration points, storage semantics, documentation strategy, or migration policy. + +== Numbering + +SKEEP files use three-digit stable numbering: + +[source,text] +---- +001-tensor-collection-literals.adoc +002-future-topic.adoc +003-future-topic.adoc +---- + +Numbers are assigned when a proposal is accepted into the docs tree, not when the underlying feature ships. + +== Status Values + +Draft:: +The proposal is being shaped and may still change substantially. + +Accepted:: +The design direction is approved, but implementation may still be pending. + +Implemented:: +The feature is available in released or releasable SKaiNET code. + +Superseded:: +A newer SKEEP replaced the design. + +Rejected:: +The proposal was considered and intentionally not pursued. + +== Proposal Template + +Every proposal should include: + +* title and SKEEP number; +* status; +* audience; +* summary; +* motivation; +* proposed design; +* compatibility and migration notes; +* rollout plan; +* acceptance criteria; +* risks; +* open questions; +* references. + +== Current Proposals + +[cols="1,1,3",options="header"] +|=== +| Number | Status | Title + +| xref:skeep:001-tensor-collection-literals.adoc[SKEEP-001] +| Draft +| Tensor collection literals +|===