|
| 1 | +# Project: ttl-language |
| 2 | + |
| 3 | +## Goal |
| 4 | +This document defines the stable language and compiler contracts for `.ttl` sources. |
| 5 | +The design target is Go-like syntax plus ergonomic TurboTasks-style core types (`Vc`-centric) for incremental, cache-first, parallel task execution. |
| 6 | + |
| 7 | +## Path |
| 8 | +- Language spec document: `docs/project-ttl-language.md` |
| 9 | +- Compiler implementation target: `cmds/ttlc` (planned in v1 documentation phase) |
| 10 | + |
| 11 | +## Runtime and Language |
| 12 | +- Language surface: TTL (`.ttl`) |
| 13 | +- v1 compiler backend: generated Go source |
| 14 | +- v1 execution/caching backend: runtime APIs + SQLite cache |
| 15 | + |
| 16 | +## Users |
| 17 | +- Engineers authoring build/task pipelines in `.ttl` |
| 18 | +- Runtime/compiler maintainers implementing deterministic incremental execution |
| 19 | + |
| 20 | +## In Scope |
| 21 | +- Lexical and syntactic contracts for `.ttl` |
| 22 | +- Typed task declaration and core runtime types |
| 23 | +- Incremental dependency tracking (`read(vc)` contract) |
| 24 | +- Hash-based invalidation model |
| 25 | +- Go code generation contract |
| 26 | +- Runtime/caching schema-level contracts |
| 27 | + |
| 28 | +## Out of Scope |
| 29 | +- Full language grammar for every future feature |
| 30 | +- Alternative backend targets in v1 |
| 31 | +- Language server protocol details |
| 32 | +- Remote/distributed scheduler protocol |
| 33 | + |
| 34 | +## Architecture |
| 35 | +Language-to-runtime architecture: |
| 36 | +- Source Layer: `.ttl` modules with Go-like declarations. |
| 37 | +- Semantic Layer: type checking and task graph extraction. |
| 38 | +- Lowering Layer: typed IR lowered to runtime API calls. |
| 39 | +- Runtime Layer: dependency tracking, scheduler, cache lookup/writeback. |
| 40 | +- Persistence Layer: SQLite metadata and optional blob storage. |
| 41 | + |
| 42 | +Compilation flow: |
| 43 | +1. Parse module declarations (`package`, `import`, `type`, `task func`, `func`). |
| 44 | +2. Resolve symbols and validate core type usage. |
| 45 | +3. Build dependency edges from `read(vc)` and task calls. |
| 46 | +4. Derive task/cache fingerprints. |
| 47 | +5. Emit Go source that invokes runtime task APIs. |
| 48 | + |
| 49 | +## Interfaces |
| 50 | +Canonical type and command identifiers: |
| 51 | + |
| 52 | +```ts |
| 53 | +enum TtlCompileTarget { |
| 54 | + GoSource = "go-source", |
| 55 | +} |
| 56 | + |
| 57 | +enum TtlCacheBackend { |
| 58 | + SQLite = "sqlite", |
| 59 | +} |
| 60 | + |
| 61 | +enum TtlCommand { |
| 62 | + Build = "build", |
| 63 | + Check = "check", |
| 64 | + Explain = "explain", |
| 65 | +} |
| 66 | + |
| 67 | +enum TtlCoreType { |
| 68 | + Vc = "vc", |
| 69 | + ResolvedVc = "resolved-vc", |
| 70 | + OperationVc = "operation-vc", |
| 71 | + TransientValue = "transient-value", |
| 72 | + State = "state", |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +Core language contracts: |
| 77 | +- File extension is always `.ttl`. |
| 78 | +- Task declaration style is fixed to `task func`. |
| 79 | +- Base syntax intentionally tracks Go style (blocks, signatures, package/import model). |
| 80 | +- Every `task func` must return `Vc[T]`. |
| 81 | +- `read(vc)` establishes dependency tracking from current task to the referenced task/cell. |
| 82 | + |
| 83 | +v1 syntax examples: |
| 84 | + |
| 85 | +```ttl |
| 86 | +package build |
| 87 | +
|
| 88 | +type Artifact struct { |
| 89 | + Path string |
| 90 | + Digest string |
| 91 | +} |
| 92 | +
|
| 93 | +task func Build(target string) Vc[Artifact] { |
| 94 | + src := read(ResolveSource(target)) |
| 95 | + digest := hash(src.Path, src.Digest) |
| 96 | + return vc(Artifact{Path: src.Path, Digest: digest}) |
| 97 | +} |
| 98 | +
|
| 99 | +func Main(target string) { |
| 100 | + val := read(Build(target)) |
| 101 | + print(val.Path) |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +Generated Go shape contract (illustrative): |
| 106 | + |
| 107 | +```go |
| 108 | +func Build(target string) runtime.Vc[Artifact] { |
| 109 | + return runtime.Task("Build", target, func(ctx runtime.TaskContext) (Artifact, error) { |
| 110 | + src, err := runtime.Read(ctx, ResolveSource(target)) |
| 111 | + if err != nil { |
| 112 | + return Artifact{}, err |
| 113 | + } |
| 114 | + digest := runtime.Hash(src.Path, src.Digest) |
| 115 | + return Artifact{Path: src.Path, Digest: digest}, nil |
| 116 | + }) |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +Invalidation contract: |
| 121 | +- Fingerprint inputs are mandatory and ordered: |
| 122 | +1. Input content hash |
| 123 | +2. Parameter hash |
| 124 | +3. Environment snapshot hash |
| 125 | +- Reuse occurs only when full fingerprint matches. |
| 126 | +- Any component mismatch triggers recomputation. |
| 127 | + |
| 128 | +Parallel execution contract: |
| 129 | +- Scheduler may execute tasks concurrently when no unresolved dependency edge exists. |
| 130 | +- Execution order is deterministic with respect to dependency constraints, not submission order. |
| 131 | + |
| 132 | +## Storage |
| 133 | +Cache backend is fixed to SQLite in v1. |
| 134 | +Minimum conceptual schema (field names are stable contract names): |
| 135 | +- `task_key` |
| 136 | +- `input_fingerprint` |
| 137 | +- `output_blob_ref` |
| 138 | +- `deps` |
| 139 | +- `metadata` |
| 140 | + |
| 141 | +Recommended conceptual tables: |
| 142 | +- `task_cache(task_key PRIMARY KEY, input_fingerprint, output_blob_ref, metadata_json, updated_at)` |
| 143 | +- `task_deps(task_key, dep_task_key)` |
| 144 | +- `cache_blobs(blob_ref PRIMARY KEY, codec, bytes, size_bytes)` |
| 145 | + |
| 146 | +## Security |
| 147 | +- Input file resolution must stay within configured workspace roots. |
| 148 | +- Cache metadata and blobs must not expose secret env values. |
| 149 | +- Corrupted cache records must fail closed (no unsafe partial decode). |
| 150 | + |
| 151 | +## Logging |
| 152 | +Compiler and runtime use `log/slog` with structured event records. |
| 153 | +Required fields: |
| 154 | +- `compile_stage` |
| 155 | +- `task_id` |
| 156 | +- `cache_key` |
| 157 | +- `cache_hit` |
| 158 | +- `invalidation_reason` |
| 159 | +- `worker_id` |
| 160 | +- `duration_ms` |
| 161 | +- `error_kind` |
| 162 | + |
| 163 | +Runtime task event baseline: |
| 164 | +- `task_scheduled` |
| 165 | +- `task_started` |
| 166 | +- `task_cache_hit` |
| 167 | +- `task_cache_miss` |
| 168 | +- `task_completed` |
| 169 | +- `task_failed` |
| 170 | + |
| 171 | +## Build and Test |
| 172 | +v1 implementation commands (applies when compiler/runtime code exists): |
| 173 | +- `go build ./cmds/ttlc/...` |
| 174 | +- `go test ./cmds/ttlc/...` |
| 175 | + |
| 176 | +Documentation acceptance checks: |
| 177 | +1. `task func` examples always return `Vc[T]`. |
| 178 | +2. `read(vc)` examples always imply dependency tracking. |
| 179 | +3. Hash invalidation explicitly includes input/parameter/environment components. |
| 180 | +4. Cache backend remains SQLite in all ttl contracts. |
| 181 | +5. Failure modes and observability fields remain explicit and stable. |
| 182 | + |
| 183 | +Failure mode contracts: |
| 184 | +- Circular dependency: fail with cycle diagnostics and no partial success state. |
| 185 | +- Cache corruption: emit `error_kind=cache_corruption`, invalidate row, recompute. |
| 186 | +- Type mismatch: fail during type-check or decode boundary with typed diagnostics. |
| 187 | +- Non-deterministic task warning: emit warning diagnostics when unstable outputs are detected. |
| 188 | + |
| 189 | +## Roadmap |
| 190 | +- Phase 1: Lock syntax + type contracts and ship Go emitter skeleton. |
| 191 | +- Phase 2: Complete scheduler/dependency tracking and SQLite persistence. |
| 192 | +- Phase 3: Improve diagnostics, performance, and cache lifecycle tooling. |
| 193 | + |
| 194 | +## Open Questions |
| 195 | +- Should future syntax include explicit task priority annotations? |
| 196 | +- Should environment snapshots be user-configurable by allowlist rules? |
| 197 | +- Should blob storage remain SQLite-only or move large payloads to file-backed blobs by default? |
| 198 | + |
| 199 | +## References |
| 200 | +- `docs/project-ttl.md` |
| 201 | +- `docs/project-template.md` |
| 202 | +- [turbo-tasks/lib.rs](https://github.com/vercel/next.js/blob/canary/turbopack/crates/turbo-tasks/src/lib.rs) |
| 203 | +- [turbo-tasks/vc/mod.rs](https://github.com/vercel/next.js/blob/canary/turbopack/crates/turbo-tasks/src/vc/mod.rs) |
| 204 | +- [turbo-tasks/value.rs](https://github.com/vercel/next.js/blob/canary/turbopack/crates/turbo-tasks/src/value.rs) |
0 commit comments