Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Agent notes

Follow [CONTRIBUTING.md](CONTRIBUTING.md), including its generator-test
notes. Also:

- Never run the `ack_generator` suite from several checkouts or worktrees at
the same time.
- Don't restart a test run that was stopped from outside; ask first.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 1.7.0-beta.2

* Add `AckModelAdapter.modelSchema`, function-parameter annotations, string
format annotations, and shared schema inference for downstream generators.
* Generate a static `Model.schema` shorthand for `Model.$ack.modelSchema` on
`@AckInfer()` models.
* Add `CodecSchema.canEncode` for direct codec encoder introspection.
* Generated `copyWith` is a getter that returns a typed interface, so a wrongly
typed argument fails to compile. Calls such as `model.copyWith(name: 'x')`
are unchanged; code that tears off or mocks the `copyWith` method must be
updated. Generated parts also suppress consumer lints and coverage.
* Constrained `Set` fields (`@MinItems`, `@MaxItems`, `@UniqueItems`) require
unique items at the JSON boundary. Duplicate input such as `['a', 'a']` now
fails validation instead of collapsing into a smaller set.
* Add explicit `description` options to `@AckModel` and `@AckField` (#175).
A single-line `@description` doc tag is a fallback. Untagged prose does not
become schema data.
* Escape control characters in generated string literals, including inferred
pattern values (#164).
* Align all six publishable packages at the 1.7.0 beta. The generator now
requires the 1.7.0 beta of `ack` and `ack_annotations`.

## 1.6.2

* Restore import of Ack-exported JSON Schemas containing `pattern` and accept
Expand Down
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ For code generation changes, also run:
dart run melos run test:gen
```

The generator suite is memory-heavy: eight tests tagged `integration` run
real `build_runner` builds in temporary packages. For a quick check while
iterating, skip them:

```bash
dart run melos run test:gen:unit
```

Run the full `test:gen` before opening the PR. Run one generator suite at a
time on a machine; running it from several checkouts in parallel can exhaust
memory.
Tag any new test that runs a real `build_runner` build with
`@Tags(['integration'])`.

For JSON Schema export changes, also run:

```bash
Expand Down
60 changes: 36 additions & 24 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ environment. A release-preparation PR does not publish anything.

Use SemVer for the combined public API: patch for compatible fixes, minor for
new compatible features/packages, and major when any stable public API breaks.
The next release is **1.6.2**, restoring JSON Schema import compatibility for
`pattern` and accepting standard `propertyNames` schemas. No public Dart API
was added; this is a patch release for the import compatibility fix. The API
check compares the six packages against v1.6.1.

The shared API baseline for this release is 1.6.1. Confirm that all six
packages have published 1.6.1 versions before preparing the release.

Melos **8.7** is configured with `mode: fixed` and `workspaceTag: true`, matching
The next candidate is **1.7.0-beta.2**. It previews shared inference, model
adapter schemas, codec introspection, typed generated `copyWith`, stricter
constrained-`Set` validation, static model schema shorthand, and explicit
description controls. The API check compares
all six packages against 1.6.2; generator compatibility also requires the
consumer build and runtime tests because the API check cannot inspect
generated consumer code.

The shared API baseline for this release is 1.6.2. Confirm that all six
packages have published 1.6.2 versions before preparing the release.

Melos **8.x** is configured with `mode: fixed` and `workspaceTag: true`, matching
this repository's release-tag verifier. `smartDependents: true` preserves
compatible dependency minimums instead of forcing unnecessary upgrades.
For example, packages at 1.3.0 can still declare `ack: ^1.2.0` when they use only
Expand Down Expand Up @@ -47,27 +50,34 @@ References: [Melos versioning](https://melos.invertase.dev/commands/version),
3. Preview/prepare a coordinated version without creating commits or tags:

```sh
dart run melos version --manual-version=ack:1.6.2 --yes --no-git-commit-version
dart run melos version --manual-version=ack:1.7.0-beta.2 --yes --no-git-commit-version
```

Use the named flag, not a positional package argument: the positional form
filters the selected packages in Melos 8.7. Choose the next SemVer value for
later releases. Normal automatic selection is also available with
selects a package instead of requesting a coordinated version. Choose the
next SemVer value for later releases. Normal automatic selection is also available with
`dart run melos version --yes --no-git-commit-version` after reviewing commits.
Do not pass `--all`: that would include private packages.
Do not pass `--all`: that would include private packages. Melos fetches tags
through Git; a new local branch needs an upstream before this command runs.

4. Review generated changelogs and retain substantive release notes; move only
unreleased notes into the new section, preserving every published section.
Update installation snippets to match each package's version.
5. Set `API_BASELINE_VERSION` in `.github/workflows/preflight.yml` to the latest
release published for all packages (currently `1.6.2`). Record a new
5. Compare against the most recent coordinated stable release in
`API_BASELINE_VERSION` in `.github/workflows/preflight.yml` (currently
`1.6.2`). Keep that stable baseline for prereleases, so a beta cannot hide a
breaking change since the last stable version. Raise it after the next
stable release. Record a new
package's actual first release in `ackPackageFirstReleases` in
`scripts/src/workspace_packages.dart`;
checks skip only older baselines. `ack_mcp_dart` first released at 1.3.0.
6. Validate the complete release:
6. Commit the reviewed version, changelog, and documentation changes locally
before the publish dry run. Pub treats modified tracked package files as a
warning, and this repository requires zero warnings.
7. Validate the complete release from that clean commit:

```sh
dart scripts/verify_release_tag.dart v1.6.2 --skip-ancestry
dart scripts/verify_release_tag.dart v1.7.0-beta.2 --skip-ancestry
dart run melos run ci
dart scripts/api_check.dart 1.6.2
dart scripts/publish_dry_run.dart
Expand All @@ -78,13 +88,13 @@ References: [Melos versioning](https://melos.invertase.dev/commands/version),
The actual release workflow always enforces ancestry on `origin/main`.
CI also runs minimum Dart/Flutter SDK checks, deterministic generation, and
staged hosted-dependency analysis; all must pass on the release merge commit.
7. Review and merge the release PR before creating the tag.
8. Review and merge the release PR before creating the tag.

## Historical first publication of ack_mcp_dart

[pub.dev requires the first version of a new package to be published manually](https://dart.dev/tools/pub/automated-publishing).
OIDC cannot create a new package. The steps below applied only to the first
`ack_mcp_dart` release, v1.3.0; do not repeat them for 1.6.2.
`ack_mcp_dart` release, v1.3.0; do not repeat them for later releases.

After the release PR merges and all checks pass, but **before pushing v1.3.0**:

Expand Down Expand Up @@ -121,9 +131,9 @@ After the release commit's CI/preflight succeeds:
```sh
git fetch origin main --tags
# Use the exact reviewed release merge commit, not an arbitrary later main head.
git tag -a v1.6.2 <release-merge-sha> -m 'Ack 1.6.2'
dart scripts/verify_release_tag.dart v1.6.2
git push origin v1.6.2
git tag -a v1.7.0-beta.2 <release-merge-sha> -m 'Ack 1.7.0 beta 2'
dart scripts/verify_release_tag.dart v1.7.0-beta.2
git push origin v1.7.0-beta.2
```

Pushing the tag triggers `.github/workflows/release.yml`; there is no Melos
Expand All @@ -148,5 +158,7 @@ missing version. Rerun failed jobs on the **same tag**; never move a published t
or republish different contents under an existing version.

After all six exact versions are visible, create the GitHub Release from the
existing tag using the prepared release notes. Before the first subsequent code
change, begin a new unreleased changelog section instead of editing 1.6.2 notes.
existing tag using the prepared release notes and mark it as a prerelease for
beta versions. Before the first subsequent code
change, begin a new unreleased changelog section instead of editing the
1.7.0-beta.2 notes.
31 changes: 31 additions & 0 deletions docs/core-concepts/typesafe-schemas.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ Generated models provide:
- `User.parse` and `User.safeParse` for validated input;
- `User.fromJson`, `toJson`, and `safeToJson` for the JSON boundary;
- generated `copyWith`, deep collection-aware `==`/`hashCode`, and `toString`;
- `User.schema`, the model-valued schema for composition such as
`Ack.list(User.schema)`; it is a shorthand for `User.$ack.modelSchema` and
is omitted when the model has a field or discriminator named `schema`, the
annotated declaration is named `schema`, or the library imports a prefix
named `schema`;
- a public `User.$ack` adapter used by nested generated models.

The generator supports object and value roots, literals, enums, lists, sets,
Expand Down Expand Up @@ -219,6 +224,32 @@ static final Profile Function(Map<String, dynamic>) fromJson =
ProfileSchema.fromJson;
```

Use `@AckModel(description: ...)` and `@AckField(description: ...)` to export
schema descriptions. A single-line `@description` tag in a `///` or `/** */`
doc comment is a fallback. Only the text after the tag, on the same line, is exported. Untagged
prose does not become schema data. Explicit annotation text takes precedence.
Blank or duplicate tags fail generation. `@description` is an Ack convention,
not a Dart annotation.

A class description applies to its object schema and model codec. This includes
sealed union bases and branches. A field description applies at its property.
It does not change a nested model's own description.

```dart
/// Tracks one unit of work. This prose is not exported.
@AckModel(description: 'A task the person can complete.')
final class Task with _$TaskAck {
const Task({required this.id, required this.title});

final String id;

/// @description What to do.
final String title;
}

TaskSchema.toJsonSchema()['description']; // 'A task the person can complete.'
```

Use `@AckModel(schemaName: 'WireProfileSchema')` to override the exact public
facade name. It must be a public UpperCamel identifier. The private backing
name remains derived from the model class, and no public lower-camel alias is
Expand Down
20 changes: 20 additions & 0 deletions example/lib/additional_properties_example.ack.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 25 additions & 23 deletions example/lib/class_first_models.ack.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion example/lib/class_first_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,39 @@ import 'package:ack_annotations/ack_annotations.dart';
part 'class_first_models.ack.dart';
part 'class_first_models.ack.g.dart';

@AckModel(caseStyle: AckCaseStyle.snake)
/// A signed-in user's account.
@AckModel(
caseStyle: AckCaseStyle.snake,
description: "A signed-in user's account.",
)
final class Account with _$AccountAck {
const Account({
required this.displayName,
this.website,
this.role = 'member',
});

/// The name shown to other users.
@MinLength(2)
@AckField(description: 'The name shown to other users.')
final String displayName;
final Uri? website;
final String role;

static final fromJson = AccountSchema.fromJson;
}

/// A pet, identified by its `type`.
/// @description A pet, identified by its `type`.
@AckModel(discriminatorKey: 'type')
sealed class Pet with _$PetAck {
const Pet({required this.id});

final String id;
}

/// A cat with a limited number of lives.
/// @description A cat with a limited number of lives.
@AckModel(discriminatorValue: 'cat')
final class Cat extends Pet with _$CatAck {
const Cat({required super.id, required this.lives});
Expand Down
Loading
Loading