← Core
Publishedlicense: BSD-3-Clause

tom_core_codespecs

tom_core_codespecs · v0.12.0

The structures CodeSpecs needs on top of tom_core that a general tom_core application does not — the one TomSpecs release-1 chain member that lives in the core tree.

View repository → See License
Status
Published
LOC
860
Tests
113
Test LOC
1.7k

Overview

From the module readme.md file:

What it enables

Enables CodeSpecs on tom_core.

Relationships

Standalone — no declared relationships.

tom_core_codespecs — the CodeSpecs gap classes for tom_core

> Cross-references. > tom_specs_model/doc/codespecs_mapping.md > owns the parts catalogue (codespecs_mapping.md §4.1), the > five-package tom_core-family basis every CodeSpec is built on > (codespecs_mapping.md §1.1) and the per-part spec-authorable attribute > surfaces (codespecs_mapping.md §5). > tom_specs_model/doc/codespecs_derivation_contract.md > owns what code each marker produces and which superclass it is built on. > This README is the inventory of what this package ships and why each class > exists; those documents own which specification section produces it and > what the generator must emit.

CodeSpecs-only gap-filler for tom_core — structures needed by CodeSpecs but not mandatory for a general tom_core project.

Where this fits

TomSpecs builds software from structured specification documents in phases; Phase 4 — CodeSpecs turns those documents into a skeletal, compilable Dart application. A rule governs that skeleton: every class it instantiates must be a tom_core-family class — one of the four packages tom_core_kernel, tom_core_flutter, tom_core_server and tom_flutter_ui (the fourth being the basis for the UI parts, codespecs_mapping.md §1.1 pillar (b)). Generated code therefore runs on the framework a hand-written Tom application runs on, rather than on a parallel one invented for generation.

tom_core_codespecs exists because that rule occasionally has nowhere to land. For a handful of parts the framework genuinely has no holder — it has the runtime half and not the authoring half — and without this package the choice would be to weaken the rule or to push CodeSpecs-only concerns into tom_core itself. This package is the third option: the concrete classes tom_core lacks, kept outside it, so tom_core stays free of concerns only a generator has.

Overview

Every part of the codespecs_mapping.md §4.1 catalogue is surveyed against tom_core with one of three verdicts — reuse (build on a shipped class), extend, or new-in-codespecs. Only the third lands here, as a concrete class; never an abstract Cs* base, because the framework in tom_code_specs carries annotations only (codespecs_mapping.md §1.1). Seven parts survive that survey: CE-CL, CE-LO, CE-TX, CE-NV, CE-JB, CE-NT and CE-RP.

> The package is dependency-free, and that is an invariant. pubspec.yaml > declares dependencies: {} and nothing under lib/ carries an import > directive at all. It is load-bearing rather than incidental: it is why > TomReportResult does not extend tom_core_server's TomTabularResult, and > why a job's CE-RP targets ride @CsJob(targetReports:) in tom_code_specs > instead of on TomJobDeclaration. A candidate gap class that would need a > tom_core_* type is therefore not a gap class — it belongs in the package > whose type it needs. Both halves are checked by > test/gap_class_inventory_test.dart, so a single added import fails the suite > rather than quietly retracting two decisions.

Installation

dependencies:
  tom_core_codespecs: ^0.11.2

or

dart pub add tom_core_codespecs
import 'package:tom_core_codespecs/tom_core_codespecs.dart';

Features

One gap-class subject per file, one file per part. The inventory is pinned to its justifications by test/gap_class_inventory_test.dart: adding or removing a file means a part changed its reuse verdict, and that has to be stated in the test next to the part it claims.

The client-application descriptor

ClassPartPurpose
TomClientApplication CE-CL Client Client-application descriptor (id, platforms, entry route, screens, server URL).

tom_core has no client-application descriptor root: TomAuthenticationData carries a client id string and TomClientRemoteContext models a live connection, but neither names the client application as a first-class, spec-authorable unit.

The catalogued gaps

Four parts the codespecs_mapping.md §4.1 catalogue marks gap, each verified against tom_core before the class was written:

ClassPartPurposeWhy the substrate does not cover it
TomLayoutModel (+ TomLayoutNode and its two subtypes TomLayoutContainerNode and TomLayoutSlotNode , TomLayoutOverrideDelta , TomLayoutContainerKind , TomLayoutDeltaOp ) CE-LO Layout (codespecs_mapping.md §5.2/§5.12/§5.22) Two-layer layout: a generated base tree plus an authored overrides layer of id-addressed deltas. reconcileWarnings() reports a delta whose target vanished or now names the wrong kind of node , never drops it. tom_flutter_ui 's AclComponent is a rendering leaf holding a live Widget — no base/override layering, no delta grammar, no id-addressed placement.
TomMessageKeyRegistry (+ TomMessageKey , TomMessageRole , TomMessageCategory ) CE-TX Text (codespecs_mapping.md §5.8/§5.21) The closed message-key catalogue. Locale fallback goes to base copy, never the raw key; an uncatalogued key is a spec error; error copy is keyed by its CE-ER code. tom_core_kernel 's TomTextResourceProvider is an untyped runtime Map<String, dynamic> — it answers "what text is behind this key", not "which keys may exist".
TomRouteRegistry (+ TomRouteDefinition , TomFormScreenAssignment , TomScreenFlowEdge , TomScreenPresentation , TomFlowOutcome ) CE-NV Navigation (codespecs_mapping.md §5.11) Route-id registry (uniqueness + dangling-reference checks) and the screen-flow model: form→screen as replace/popup, action targets conditional on outcome. tom_flutter_ui 's TomPageRoute is a bare MaterialPageRoute subclass with no route id and no registry — the framework assumes uniqueness with nowhere to check it.
TomJobDeclaration CE-JB Job (codespecs_mapping.md §5.29) The deployment/ownership envelope around a job: enabled , environments , serviceUnitId , and its CE-DB entity targets as Type literals ( readEntities / writtenEntities , with targetEntities the union). Its CE-RP report targets ride @CsJob(targetReports:) in tom_code_specs instead — codespecs_mapping.md §5.23 makes the Cs*Ref family the annotation parameter vocabulary, which is also what keeps this package dependency-free. tom_core_kernel 's scheduling module supplies the entire runtime half ( TomJobBase , TomJobDefinition , TomSchedule , TomScheduler , TomJobStore , TomLeaseLock , TomJobDispatcher ) and is reused as-is; what it has no home for is the spec-side decision to deploy the job at all.

The two promoted parts

Both were codespecs_mapping.md §4.3 deferred candidates. The codespecs_mapping.md §8.1 authored-input test decides which of those can be built at all — a candidate whose SOM section is free text gives a generator nothing to read.

ClassPartPurposeWhy the substrate does not cover it
TomNotificationType , TomNotificationChannelDeclaration , TomNotificationPreferences , TomNotificationCatalog (+ TomNotificationCategory , TomNotificationUrgency ) CE-NT Notification (codespecs_mapping.md §4.3.2) The choosing layer: which notification types exist, which channels each goes out on, and how user preferences narrow that set. channelsFor resolves type constraints against preferences (mandatory first); deliveryChannelsFor adds the channel's own urgency ceiling. tom_core_server 's messaging module ( TomMessage , TomMessageChannel , TomMessageRouter , TomMessageOutbox ) delivers — it takes an already-composed message on an already-chosen channel. It is reused as-is; the choosing is what a specification authors.
TomReportDefinition (+ TomReportDimension , TomReportMeasure , TomReportColumn , TomReportParameter , TomReportChart , TomReportValueKind , TomReportMeasureFunction , TomReportDeliveryChannel , TomReportChartKind ) CE-RP Reporting (codespecs_mapping.md §5.28) The grouped projection a specification authors — dimension by dimension, measure by measure — plus the output columns that display them, the per-execution parameters, and the charts declared over the projection. TomGroupedSelect is a query , compiled from an already-decided grouping. Nothing in tom_core holds the decision itself, nor the report's identity or delivery channels.
TomReportResult (+ TomReportResultSection) CE-RP Reporting (codespecs_mapping.md §5.28) The shared result envelope: sections of columns over a Stream of rows, plus the chart declarations and run metadata. tom_core_server 's TomTabularResult is server-side, and the envelope's locus is shared — the client renders a report too.

CE-LG was promoted alongside CE-NT but is a reuse verdict — @CsAudited marks the framework's own @TomAudited — so it needs no gap class here.

Quick start

A gap class is an ordinary Dart class with no framework around it. The CE-TX catalogue is the smallest one that shows why the part is a gap at all: it answers which keys may exist, which the runtime lookup store cannot.

// dart run example.dart
import 'package:tom_core_codespecs/tom_core_codespecs.dart';

void main() {
  final texts = TomMessageKeyRegistry(baseLocale: 'en')
    ..register(
      TomMessageKey(
        key: 'app.error.amount.tooLarge',
        baseCopy: 'Amount must not exceed {max}.',
        role: TomMessageRole.error,
        category: TomMessageCategory.errorCopy,
        errorCode: 'AMOUNT_TOO_LARGE',
        parameters: ['max'],
        localeCopy: {'de': 'Betrag darf {max} nicht überschreiten.'},
      ),
    );

  print(texts.resolve('app.error.amount.tooLarge', 'de'));
  // Betrag darf {max} nicht überschreiten.

  // A locale with no translation falls back to base copy — never to the key.
  print(texts.resolve('app.error.amount.tooLarge', 'fr'));
  // Amount must not exceed {max}.

  print(texts.forErrorCode('AMOUNT_TOO_LARGE')?.key);
  // app.error.amount.tooLarge

  print(texts.hasKey('app.error.unlisted'));
  // false — and resolving it throws, because the catalogue is closed
}

Usage

Deciding whether something belongs here

The question is not "does CodeSpecs need this" but "does tom_core already hold it". Four verdicts recur, and each has a settled answer:

VerdictWhat it meansExample
reuse A shipped tom_core class is the holder. CE-AU on TomAuthenticationService, CE-CF on TomBaseServerConfiguration
reuse via annotation The part is markers over shipped classes; nothing to hold. CE-DB — @CsTable / @CsColumn / @CsRepository over tom_core_server persistence
no holder needed Every attribute lives on the annotated member, an annotation argument, or another part. CE-UP, domain enums
gap Nothing holds it. A concrete class lands here. The seven parts above

A candidate that would need to import a tom_core_* type fails the dependency-free invariant and is not a gap class at all — it belongs in the package whose type it needs.

The reuse verdicts worth stating explicitly

CE-CC (ClientConfiguration). tom_core_flutter's TomBaseClientConfiguration / TomSetting<T> / TomClientConfigurationStore is the (client app, machine) holder — typed declaration through declareSettings(), baselines resolved from TomConfigResourceProvider, overrides persisted and only overrides, and observation both per field and holder-wide — with no user in the key, which is the whole CE-CC discriminator (codespecs_mapping.md §4.1, §5.16, §11). This is the drift the inventory test was introduced to close: a TomClientConfiguration was still exported here after TomBaseClientConfiguration landed in tom_core_flutter.

CE-DB (Data). A reuse verdict — the markers are built directly on tom_core_server persistence classes (@TomDbTable, @TomDbColumn, @TomEntityId, @TomDbScope, TomRepository<T,K>, TomSqlDatasourceRepository, query_builder, TomTransaction).

Domain enums. codespecs_mapping.md §4.1 realises the domainEnum member kind as a plain Dart enum, so there is no class to build on and none to supply. A domain enum has no authored attributes of its own to hold:

Would-be attributeWhere it actually lives
value token (valueId) the enum constant's own identifier — no second string to drift from the name
value label CE-TX copy in the message-key registry, like every other label ( codespecs_mapping.md §1.2 consequence 1, codespecs_mapping.md §5.21)
description the doc comment the generator writes above the enum and each constant ( codespecs_derivation_contract.md §3.1.1)
default value the enum-typed member that has one (a CE-DB column, a CE-CF/CE-CC/CE-DS/CE-UP setting, a CE-ST field) — two members of the same type may default differently

A holder would hold nothing, and it would give value labels a second home, which the closed-vocabulary rule forbids.

CE-AC (Trigger). codespecs_mapping.md §4.1 / §5.10 / §5.20 all record "no gap — full action implementation reused": the trigger taxonomy is a documented classification over the reused tom_flutter_ui action classes. Its closed five-kind vocabulary therefore ships as CsTriggerKind on @CsTrigger in tom_code_specs, not as a class here.

CE-UP (UserSetting). codespecs_mapping.md §11 splits configuration and settings into four parts, one per scope key, each single-moded: CE-CF (server/system), CE-CC (client app + machine, no user), CE-DS (user + device, device-persisted), CE-UP (user, server-persisted). The scope key alone decides where a value lives, so no part carries a persistence discriminator — the choice is which part you use. A local/roaming mode on a CE-UP class would be a second way to say what the part already says: the local arm is CE-DS, which codespecs_mapping.md §4.1 marks reuse — no new class. Its whole surface resolves onto shipped classes and onto the annotated member itself:

CE-UP surfaceWhere it lives
the setting's key @CsUserSetting('user.preferredLanguage') 's own argument, against TomUserPreferenceKeys ( tom_core_kernel )
its type and default the annotated member — String preferredLanguage = 'de' ( codespecs_mapping.md §5.16), exactly as a domain enum's default belongs to the member that has one
the user's persisted value TomUserPreferences ( tom_core_server ) over TomUserPreferenceRepository , scoped by @TomDbScope(field: 'principal_id')
the wire tomUserPreferencesApi 's four endpoints and TomUserPreferenceDto ( tom_core_kernel )
codespecs_mapping.md §5.16's persisted-value → default order TomUserPreferences.getOr(key, <the member's initialiser>)
the setting's display name CE-TX copy in the message-key registry ( codespecs_mapping.md §1.2 consequence 1, codespecs_mapping.md §5.21) — a label has one home

A persistence seam here would also be strictly worse than the shipped one: an in-memory map with no scope and no rows, standing in front of a persistent, principal-scoped service. Note that TomGetSettingsMessage / TomGetSettingsResult is not that seam and never was — it is the client bootstrap payload (texts, config, groups, roles, entitlements), read-only and not per-user-preference.

Placing a class in the generated trio

Phase 4 emits three projects — shared, client-only and server-only (codespecs_mapping.md §4.2) — and a gap class is authored against whichever locus its part carries.

Entity-model placement (CE-DB). A CodeSpecs data entity is referenced by both the client (as DTO/view) and the server (as persistence). The entity's field shape is authored once in the shared project as the canonical entity; the server-only @CsTable/@CsColumn/@CsRepository persistence projection and the client-only DTO/view both derive from that shared shape. This keeps the client free of any dependency on a server-authored type and mirrors CE-UP, whose key and value type are shared while only its persistence is server-side. The persistence markers themselves remain server-side parts.

The same rule applied to results. TomReportResult deliberately does not extend TomTabularResult: a shared type cannot depend on a server type, so the server adapts one way onto the tabular shape when exporting. The adaptation is honoured in one inherited constraint — rows stay a Stream, because a report exists for results too large to hold inline. A section is built from a Stream Function() rather than a stream, since deliveryChannels is a list and one run delivered twice must read its rows twice.

Typed edges ride the annotation, not the class. A report's authorization is not held here; it rides a @CsAuthorize beside the @CsReport marker, per codespecs_mapping.md §5.15's field-level rule, so the access level and the permitted roles are that annotation's requirement kind and its typed CsRoleRef list and the edge is compile-checked instead of being an id string on the definition. TomJobDeclaration follows the same rule: tom_core-family classes hold plain ids, and an edge that wants a typed ref belongs on the annotation.

Architecture

tom_core_codespecs                    (dependencies: {} — no imports at all)
└── lib/src/
    ├── client_application.dart   CE-CL  TomClientApplication
    ├── job_declaration.dart      CE-JB  TomJobDeclaration
    ├── layout_node.dart          CE-LO  TomLayoutModel + node tree + deltas
    ├── message_key.dart          CE-TX  TomMessageKeyRegistry + entries
    ├── notification_model.dart   CE-NT  TomNotificationCatalog + types
    ├── report_model.dart         CE-RP  TomReportDefinition + TomReportResult
    └── route_flow.dart           CE-NV  TomRouteRegistry + screen flow

  a CodeSpec class          is a          gap class here
        │                                       │
        └── marked by a Cs* marker ─────────────┘
            (tom_code_specs)          … or, far more often, is a
                                      shipped tom_core-family class
TypeResponsibility
TomClientApplication The client application as a spec-authorable unit: id, platforms, entry route, screen ids, server base URL.
TomLayoutModel The base/override two-layer layout, with reconciliation that reports orphaned deltas rather than dropping them.
TomMessageKeyRegistry The closed message-key catalogue: which keys may exist, their role and category, and which CE-ER code each error string renders.
TomRouteRegistry Route-id uniqueness and dangling-reference checking, plus the screen-flow edges between forms and screens.
TomJobDeclaration The spec-side decision to deploy a job at all, and the entity targets it reads and writes.
TomNotificationCatalog Which notification types exist and which channels each may go out on, narrowed by preferences.
TomReportDefinition The grouped projection a specification authors — dimensions, measures, columns, parameters and charts.
TomReportResult The shared result envelope both the server and the client read.

Where consistency is settled

Construction is where a class's internal consistency is settled: constructors throw ArgumentError on each specification defect no generator can repair — anything that would leave the emitted code referring to something that does not exist. TomReportDefinition's constructor dartdoc enumerates its cases, and is the one place that does; a count repeated in prose is a count that goes stale. TomReportMeasureFunction is 1:1 with tom_core_server's TomAggregateFunction — a measure the query layer cannot compile is a measure that cannot run — and COUNT(*) / COUNT(DISTINCT c) are expressed as fields (columnName, distinct) rather than as extra members.

Essentiality is likewise a property of the type (isEssential), not of the preferences: a type the user cannot configure, or one with a mandatory channel, survives a blanket opt-out whole. Silencing is therefore all-or-nothing per type and can only ever reach a non-essential one — never a per-channel narrowing of an essential type.

Ecosystem

  tom_core family                  the substrate a CodeSpec is built on
  kernel / flutter / server /
  tom_flutter_ui
        │
        │ what it lacks
        ▼
  tom_core_codespecs               ← this package: 7 concrete gap classes
        ▲
        │ built on
  generated CodeSpecs trio         shared / client / server
        ▲
        │ marked by
  tom_code_specs                   the Cs* annotation family
        ▲
        │ routed by
  tom_specs_model                  the SOM sections a part is derived from

Further documentation

TomSpecs subject matter — the authorities behind every verdict here:

DocumentAuthority for
index.md The catalogue of the whole TomSpecs document set, and the § citation convention
codespecs_mapping.md The parts catalogue, the five-package tom_core -family basis, the attribute surfaces, the three-project trio, the deferred candidates
codespecs_derivation_contract.md What code comes out per marker — inputs, emitted Dart, superclass, naming rules, validator checks
codespecs_prompt.md When a Phase-4 run may begin at all — the mechanical gate and the per-area judgment
tom_specs_project_flow.md The phase model Phase 4 sits in, and the quality gate it must pass

This package — its own guides:

GuideCovers
doc/index.md The catalogue of this package's documentation
doc/gap_classes.md The seven modules, the dependency-free invariant, and where each kind of error surfaces
doc/api/api_summary_tom_core_codespecs.md Every class and enum, its constructor, properties and methods

Runnable examples — one per gap class, under example/:

dart run --enable-asserts example/run_all_samples.dart

The flag is not cosmetic. Every example states its expected outcome with assert and nothing else, and dart run strips asserts — so a flagless run prints the same transcript, exits 0 and verifies nothing. Each example refuses that run rather than letting it look like a pass, and test/examples_test.dart runs all seven plus the aggregator under exactly the command their headers document. It also holds the folder to the gap-class inventory in both directions, so a gap class without an example is a failure rather than an omission.

Siblings — packages you will reach for next:

PackageWhat it is
tom_code_specs The Cs* annotation family that marks a class as a part, and the typed cross-part references
tom_specs_core The SOM annotation vocabulary, and the home of @CodeSpecKind / CodeSpecPart
tom_specs_model The SOM source model whose sections route into these parts
tom_specs_clitool The CodeSpecs validator that checks a generated trio against the derivation contract

Status

Version 0.12.0, published on pub.dev. The suite runs with dart test; no count is given, because a count is a claim nothing checks and every added test would be a README edit nobody makes. The version is checked.

Every part the codespecs_mapping.md §4.1 catalogue marks gap has its class here — CE-CL, CE-LO, CE-TX, CE-NV, CE-JB, CE-NT and CE-RP. Every other part resolves onto shipped tom_core classes and needs none. CE-WF is the sole remaining codespecs_mapping.md §4.3 deferred candidate, and it is deferred permanently; a class is added here only if a further part is ever promoted out of codespecs_mapping.md §4.3.

License
BSD 3-Clause License

Copyright (c) 2024-2026, Peter Nicolai Alexis Kyaw
Find me on LinkedIn under Alexis Kyaw
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.