ossplate 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/bin/darwin-arm64/ossplate +0 -0
- package/package.json +1 -1
- package/scaffold/.github/workflows/publish-npm.yml +7 -2
- package/scaffold/.github/workflows/publish.yml +8 -3
- package/scaffold/CONTRIBUTING.md +6 -1
- package/scaffold/README.md +4 -1
- package/scaffold/assets/illustrations/armour05platemail.png +0 -0
- package/scaffold/core-rs/Cargo.lock +1 -1
- package/scaffold/core-rs/Cargo.toml +1 -1
- package/scaffold/core-rs/src/main.rs +21 -2
- package/scaffold/docs/README.md +9 -17
- package/scaffold/docs/adrs/0001-rust-core-thin-wrappers.md +21 -0
- package/scaffold/docs/adrs/0002-sync-owns-bounded-identity.md +31 -0
- package/scaffold/docs/adrs/0003-curated-scaffold-payload.md +21 -0
- package/scaffold/docs/architecture.md +87 -0
- package/scaffold/docs/customizing-the-template.md +11 -34
- package/scaffold/docs/releases.md +71 -0
- package/scaffold/docs/testing.md +5 -5
- package/scaffold/wrapper-js/README.md +4 -0
- package/scaffold/wrapper-js/bin/darwin-arm64/ossplate +0 -0
- package/scaffold/wrapper-js/package-lock.json +2 -2
- package/scaffold/wrapper-js/package.json +1 -1
- package/scaffold/wrapper-py/README.md +4 -0
- package/scaffold/wrapper-py/pyproject.toml +1 -1
- package/scaffold/wrapper-py/src/ossplate/bin/darwin-arm64/ossplate +0 -0
- package/scaffold/assets/illustrations/chestplate.svg +0 -46
- package/scaffold/docs/phase-1-contract.md +0 -52
- package/scaffold/docs/upgrade-plan.md +0 -88
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Ossplate
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png" alt="Ossplate armor" width="320">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
`ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
|
|
4
8
|
|
|
5
9
|
Use it to:
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -44,8 +44,13 @@ jobs:
|
|
|
44
44
|
env:
|
|
45
45
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
46
46
|
run: |
|
|
47
|
+
logfile=$(mktemp)
|
|
48
|
+
cleanup() {
|
|
49
|
+
rm -f "$logfile"
|
|
50
|
+
}
|
|
51
|
+
trap cleanup EXIT
|
|
47
52
|
set +e
|
|
48
|
-
npm publish --access public --provenance 2>&1 | tee
|
|
53
|
+
npm publish --access public --provenance 2>&1 | tee "$logfile"
|
|
49
54
|
status=${PIPESTATUS[0]}
|
|
50
55
|
set -e
|
|
51
56
|
if [ "$status" -eq 0 ]; then
|
|
@@ -56,7 +61,7 @@ jobs:
|
|
|
56
61
|
echo "::error title=npm::OIDC publish failed and NPM_TOKEN is not configured."
|
|
57
62
|
exit "$status"
|
|
58
63
|
fi
|
|
59
|
-
if grep -qiE "already exists|cannot publish over|previously published"
|
|
64
|
+
if grep -qiE "already exists|cannot publish over|previously published" "$logfile"; then
|
|
60
65
|
echo "::notice title=npm::package version already exists; skipping."
|
|
61
66
|
exit 0
|
|
62
67
|
fi
|
|
@@ -74,23 +74,28 @@ jobs:
|
|
|
74
74
|
env:
|
|
75
75
|
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token || secrets.CARGO_TOKEN }}
|
|
76
76
|
run: |
|
|
77
|
+
logfile=$(mktemp)
|
|
78
|
+
cleanup() {
|
|
79
|
+
rm -f "$logfile"
|
|
80
|
+
}
|
|
81
|
+
trap cleanup EXIT
|
|
77
82
|
if [ -n "${{ steps.auth.outputs.token }}" ]; then
|
|
78
83
|
echo "::notice title=cargo::using crates.io OIDC token from trusted publishing."
|
|
79
84
|
else
|
|
80
85
|
echo "::notice title=cargo::OIDC token unavailable; falling back to CARGO_TOKEN secret."
|
|
81
86
|
fi
|
|
82
87
|
set +e
|
|
83
|
-
cargo publish 2>&1 | tee
|
|
88
|
+
cargo publish 2>&1 | tee "$logfile"
|
|
84
89
|
status=${PIPESTATUS[0]}
|
|
85
90
|
set -e
|
|
86
91
|
if [ "$status" -eq 0 ]; then
|
|
87
92
|
exit 0
|
|
88
93
|
fi
|
|
89
|
-
if grep -q "status 429 Too Many Requests"
|
|
94
|
+
if grep -q "status 429 Too Many Requests" "$logfile"; then
|
|
90
95
|
echo "::warning title=cargo::crates.io rate limit hit. Treating this publish attempt as non-blocking."
|
|
91
96
|
exit 0
|
|
92
97
|
fi
|
|
93
|
-
if grep -qi "already exists on crates.io index"
|
|
98
|
+
if grep -qi "already exists on crates.io index" "$logfile"; then
|
|
94
99
|
echo "::notice title=cargo::crate version already exists; skipping."
|
|
95
100
|
exit 0
|
|
96
101
|
fi
|
package/scaffold/CONTRIBUTING.md
CHANGED
|
@@ -21,7 +21,12 @@ cargo run --manifest-path core-rs/Cargo.toml -- sync --check
|
|
|
21
21
|
cargo run --manifest-path core-rs/Cargo.toml -- sync
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Read this next:
|
|
25
|
+
|
|
26
|
+
- [`docs/architecture.md`](./docs/architecture.md)
|
|
27
|
+
- [`docs/testing.md`](./docs/testing.md)
|
|
28
|
+
- [`docs/releases.md`](./docs/releases.md)
|
|
29
|
+
- [`docs/adrs/0002-sync-owns-bounded-identity.md`](./docs/adrs/0002-sync-owns-bounded-identity.md)
|
|
25
30
|
|
|
26
31
|
Current ownership model:
|
|
27
32
|
|
package/scaffold/README.md
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
Build one project, ship it everywhere.
|
|
5
5
|
<!-- ossplate:readme-identity:end -->
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png" alt="Ossplate armor" width="360">
|
|
9
|
+
</p>
|
|
8
10
|
|
|
9
11
|
`ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
|
|
10
12
|
|
|
@@ -69,6 +71,7 @@ The same command surface is available through the Rust binary and the packaged J
|
|
|
69
71
|
## Learn More
|
|
70
72
|
|
|
71
73
|
- [Documentation](./docs/README.md)
|
|
74
|
+
- [Adoption Guide](./docs/customizing-the-template.md)
|
|
72
75
|
- [Testing Guide](./docs/testing.md)
|
|
73
76
|
- [Release Guide](./docs/releases.md)
|
|
74
77
|
- [Architecture](./docs/architecture.md)
|
|
Binary file
|
|
@@ -1041,9 +1041,18 @@ fn validate_wrapper_readme(
|
|
|
1041
1041
|
}
|
|
1042
1042
|
|
|
1043
1043
|
fn render_wrapper_readme(_language: &str, config: &ToolConfig) -> String {
|
|
1044
|
+
let image_url = github_raw_url(
|
|
1045
|
+
&config.project.repository,
|
|
1046
|
+
"dev",
|
|
1047
|
+
"assets/illustrations/armour05platemail.png",
|
|
1048
|
+
);
|
|
1044
1049
|
format!(
|
|
1045
1050
|
r#"# {name}
|
|
1046
1051
|
|
|
1052
|
+
<p align="center">
|
|
1053
|
+
<img src="{image_url}" alt="{name} armor" width="320">
|
|
1054
|
+
</p>
|
|
1055
|
+
|
|
1047
1056
|
`{command}` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
|
|
1048
1057
|
|
|
1049
1058
|
Use it to:
|
|
@@ -1071,6 +1080,7 @@ Learn more:
|
|
|
1071
1080
|
"#,
|
|
1072
1081
|
name = config.project.name,
|
|
1073
1082
|
command = config.packages.command,
|
|
1083
|
+
image_url = image_url,
|
|
1074
1084
|
)
|
|
1075
1085
|
}
|
|
1076
1086
|
|
|
@@ -1081,6 +1091,15 @@ fn render_root_readme_identity(config: &ToolConfig) -> String {
|
|
|
1081
1091
|
)
|
|
1082
1092
|
}
|
|
1083
1093
|
|
|
1094
|
+
fn github_raw_url(repository: &str, branch: &str, path: &str) -> String {
|
|
1095
|
+
let trimmed = repository.trim_end_matches('/');
|
|
1096
|
+
if let Some(rest) = trimmed.strip_prefix("https://github.com/") {
|
|
1097
|
+
format!("https://raw.githubusercontent.com/{rest}/{branch}/{path}")
|
|
1098
|
+
} else {
|
|
1099
|
+
format!("{trimmed}/{path}")
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1084
1103
|
fn validate_workflow_name(
|
|
1085
1104
|
path: &str,
|
|
1086
1105
|
expected_name: &str,
|
|
@@ -1405,7 +1424,7 @@ mod tests {
|
|
|
1405
1424
|
target.join("core-rs/Cargo.toml"),
|
|
1406
1425
|
r#"[package]
|
|
1407
1426
|
name = "bad-core"
|
|
1408
|
-
version = "0.1.
|
|
1427
|
+
version = "0.1.3"
|
|
1409
1428
|
"#,
|
|
1410
1429
|
)
|
|
1411
1430
|
.unwrap();
|
|
@@ -1541,7 +1560,7 @@ command = "ossplate"
|
|
|
1541
1560
|
root.join("core-rs/Cargo.toml"),
|
|
1542
1561
|
r#"[package]
|
|
1543
1562
|
name = "ossplate"
|
|
1544
|
-
version = "0.1.
|
|
1563
|
+
version = "0.1.3"
|
|
1545
1564
|
edition = "2021"
|
|
1546
1565
|
authors = ["Stef <stefdevscore@github.com>"]
|
|
1547
1566
|
description = "A practical baseline for shipping one project across Cargo, npm, and PyPI without starting from scratch every time."
|
package/scaffold/docs/README.md
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
# Documentation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`ossplate` is a tool for shipping one CLI across Cargo, npm, and PyPI without maintaining three separate implementations.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Start here:
|
|
6
6
|
|
|
7
|
-
- [
|
|
7
|
+
- [Adoption Guide](./customizing-the-template.md)
|
|
8
8
|
- [Architecture](./architecture.md)
|
|
9
|
-
- [Testing
|
|
10
|
-
- [
|
|
11
|
-
- [Phase 1 Contract](./phase-1-contract.md)
|
|
12
|
-
- [Upgrade Plan](./upgrade-plan.md)
|
|
13
|
-
- `ossplate validate` checks owned metadata drift
|
|
14
|
-
- `ossplate sync --check` verifies the repo is already synchronized
|
|
15
|
-
- `ossplate create <target>` scaffolds a clean target directory and can apply identity overrides from flags
|
|
16
|
-
- `ossplate init --path <dir>` hydrates an existing directory in place and can apply identity overrides from flags
|
|
9
|
+
- [Testing](./testing.md)
|
|
10
|
+
- [Releases](./releases.md)
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
Decision records:
|
|
19
13
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- the release operator flow and rerun-safe publish expectations
|
|
24
|
-
- the phased plan for turning this tool into a broader scaffold product
|
|
14
|
+
- [ADR 0001: Rust Core, Thin Wrappers](./adrs/0001-rust-core-thin-wrappers.md)
|
|
15
|
+
- [ADR 0002: Sync Owns Bounded Identity Surfaces](./adrs/0002-sync-owns-bounded-identity.md)
|
|
16
|
+
- [ADR 0003: Ship A Curated Scaffold Payload](./adrs/0003-curated-scaffold-payload.md)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# ADR 0001: Rust Core, Thin Wrappers
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`ossplate` has to ship one command surface across Cargo, npm, and PyPI. Maintaining separate implementations would create drift quickly and make release verification harder.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
- Rust is the only product implementation.
|
|
14
|
+
- JavaScript and Python packages are adapters that resolve the packaged binary and forward arguments.
|
|
15
|
+
- Wrapper packages preserve stdout, stderr, and exit status from the Rust binary.
|
|
16
|
+
|
|
17
|
+
## Consequences
|
|
18
|
+
|
|
19
|
+
- New product behavior goes into `core-rs`.
|
|
20
|
+
- JS and Python should stay small and operationally focused.
|
|
21
|
+
- Parity testing can compare wrapper execution with direct Rust execution.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ADR 0002: Sync Owns Bounded Identity Surfaces
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`ossplate sync` must keep shared project identity aligned without becoming a destructive repo rewriter.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
`sync` owns only bounded, identity-bearing surfaces:
|
|
14
|
+
|
|
15
|
+
- Rust, npm, and Python package metadata
|
|
16
|
+
- wrapper package README content
|
|
17
|
+
- the root README identity block between `ossplate:readme-identity` markers
|
|
18
|
+
- workflow display names between `ossplate:workflow-name` markers
|
|
19
|
+
|
|
20
|
+
`sync` does not own:
|
|
21
|
+
|
|
22
|
+
- workflow logic
|
|
23
|
+
- auth setup
|
|
24
|
+
- arbitrary prose outside markers
|
|
25
|
+
- docs that are not explicitly bounded
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
- Drift checks stay surgical.
|
|
30
|
+
- Contributors can change most prose and workflow logic safely.
|
|
31
|
+
- Any ownership expansion must be explicit and bounded first.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# ADR 0003: Ship A Curated Scaffold Payload
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`create` and `init` need scaffold content even when `ossplate` is installed from npm or PyPI. A broad repo snapshot would work, but it would also ship tests, maintainer-only files, and accidental junk.
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
|
|
13
|
+
- Installed wrapper artifacts ship a curated scaffold payload.
|
|
14
|
+
- `scaffold-manifest.json` is the allowlist for that payload.
|
|
15
|
+
- Artifact tests assert both required content and excluded content.
|
|
16
|
+
|
|
17
|
+
## Consequences
|
|
18
|
+
|
|
19
|
+
- Installed distributions can create and initialize projects end to end.
|
|
20
|
+
- Package contents stay intentional.
|
|
21
|
+
- Adding new scaffold files requires updating the manifest and tests.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
`ossplate` is intentionally small. The design goal is simple: one real CLI, three distribution channels.
|
|
4
|
+
|
|
5
|
+
## Runtime Shape
|
|
6
|
+
|
|
7
|
+
- Rust in [`core-rs/`](../core-rs) is the product.
|
|
8
|
+
- JavaScript in [`wrapper-js/`](../wrapper-js) is a package adapter.
|
|
9
|
+
- Python in [`wrapper-py/`](../wrapper-py) is a package adapter.
|
|
10
|
+
- The scaffold payload bundled into the wrappers is a distribution asset, not another implementation.
|
|
11
|
+
|
|
12
|
+
The main commands are:
|
|
13
|
+
|
|
14
|
+
- `version`
|
|
15
|
+
- `validate`
|
|
16
|
+
- `sync`
|
|
17
|
+
- `create`
|
|
18
|
+
- `init`
|
|
19
|
+
|
|
20
|
+
## Responsibilities
|
|
21
|
+
|
|
22
|
+
### Rust
|
|
23
|
+
|
|
24
|
+
- command parsing
|
|
25
|
+
- project identity loading from `ossplate.toml`
|
|
26
|
+
- validation logic
|
|
27
|
+
- metadata synchronization
|
|
28
|
+
- scaffold creation and initialization
|
|
29
|
+
|
|
30
|
+
Rust is the only layer that should know the semantics of project identity and owned metadata surfaces.
|
|
31
|
+
|
|
32
|
+
### JavaScript and Python
|
|
33
|
+
|
|
34
|
+
The wrappers own:
|
|
35
|
+
|
|
36
|
+
- packaged binary lookup
|
|
37
|
+
- platform/architecture target resolution
|
|
38
|
+
- local binary override support
|
|
39
|
+
- forwarding stdout, stderr, and exit code unchanged
|
|
40
|
+
|
|
41
|
+
They should not implement separate business logic, metadata rules, or command behavior.
|
|
42
|
+
|
|
43
|
+
### Scaffold Payload
|
|
44
|
+
|
|
45
|
+
The scaffold payload owns the generated-project baseline:
|
|
46
|
+
|
|
47
|
+
- manifests
|
|
48
|
+
- docs
|
|
49
|
+
- workflows
|
|
50
|
+
- wrapper launchers
|
|
51
|
+
- packaged binaries needed for installed-wrapper scaffold operations
|
|
52
|
+
|
|
53
|
+
It is curated by manifest and shipped as part of the wrapper artifacts so `create` and `init` work from installed distributions.
|
|
54
|
+
|
|
55
|
+
## Ownership Boundaries
|
|
56
|
+
|
|
57
|
+
`ossplate sync` owns only bounded identity-bearing surfaces. The details are in the ADRs, but the practical rule is simple: if a surface is not explicitly bounded, `sync` should not rewrite it.
|
|
58
|
+
|
|
59
|
+
Today that includes:
|
|
60
|
+
|
|
61
|
+
- Cargo, npm, and Python metadata fields
|
|
62
|
+
- wrapper package README identity
|
|
63
|
+
- the root README identity block
|
|
64
|
+
- workflow display names between `ossplate:workflow-name` markers
|
|
65
|
+
|
|
66
|
+
It does not own:
|
|
67
|
+
|
|
68
|
+
- workflow logic
|
|
69
|
+
- auth setup
|
|
70
|
+
- arbitrary prose outside bounded markers
|
|
71
|
+
- separate wrapper-specific product behavior
|
|
72
|
+
|
|
73
|
+
## Why It Scales
|
|
74
|
+
|
|
75
|
+
If the tool grows, keep the current rules:
|
|
76
|
+
|
|
77
|
+
- add product behavior in Rust
|
|
78
|
+
- treat wrappers as delivery adapters
|
|
79
|
+
- expand scaffold ownership only where the boundary is explicit and non-destructive
|
|
80
|
+
|
|
81
|
+
That gives the project a clean path toward a fuller hexagonal structure without forcing that complexity into the current starter.
|
|
82
|
+
|
|
83
|
+
## Related Decisions
|
|
84
|
+
|
|
85
|
+
- [ADR 0001: Rust Core, Thin Wrappers](./adrs/0001-rust-core-thin-wrappers.md)
|
|
86
|
+
- [ADR 0002: Sync Owns Bounded Identity Surfaces](./adrs/0002-sync-owns-bounded-identity.md)
|
|
87
|
+
- [ADR 0003: Ship A Curated Scaffold Payload](./adrs/0003-curated-scaffold-payload.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Adoption Guide
|
|
2
2
|
|
|
3
|
-
Use this
|
|
3
|
+
Use this guide after creating or cloning a project managed by `ossplate`. The goal is to adopt the scaffold under your own identity and then let the tool keep owned metadata aligned.
|
|
4
4
|
|
|
5
5
|
## Canonical Source Of Truth
|
|
6
6
|
|
|
@@ -22,15 +22,6 @@ It currently owns:
|
|
|
22
22
|
|
|
23
23
|
`ossplate sync` rewrites the owned surfaces back into alignment.
|
|
24
24
|
|
|
25
|
-
## Validation Policy
|
|
26
|
-
|
|
27
|
-
- Template-source identities are allowed only when the repository is discussing `ossplate` itself.
|
|
28
|
-
- Inherited identities are not allowed in shipping metadata or package-facing docs for an adopted project.
|
|
29
|
-
- Command and package naming must be chosen intentionally before release.
|
|
30
|
-
- Author, repository, and license fields must be set explicitly rather than inherited accidentally.
|
|
31
|
-
|
|
32
|
-
The current validator follows this policy through the Rust core rather than a standalone JS rule engine.
|
|
33
|
-
|
|
34
25
|
## Required Identity Changes
|
|
35
26
|
|
|
36
27
|
Replace these inherited defaults before reuse:
|
|
@@ -45,17 +36,6 @@ Replace these inherited defaults before reuse:
|
|
|
45
36
|
| Author/email | `Stef <stefdevscore@github.com>` / `stefdevscore@github.com` | Rust, npm, Python metadata |
|
|
46
37
|
| Package-facing scaffold branding | `ossplate` identity in wrapper docs | `wrapper-js/README.md`, `wrapper-py/README.md` |
|
|
47
38
|
|
|
48
|
-
## Files To Review
|
|
49
|
-
|
|
50
|
-
- `README.md`
|
|
51
|
-
- `ossplate.toml`
|
|
52
|
-
- `core-rs/Cargo.toml`
|
|
53
|
-
- `wrapper-js/package.json`
|
|
54
|
-
- `wrapper-js/README.md`
|
|
55
|
-
- `wrapper-py/pyproject.toml`
|
|
56
|
-
- `wrapper-py/README.md`
|
|
57
|
-
- `.github/workflows/*.yml`
|
|
58
|
-
|
|
59
39
|
## What `ossplate validate` Enforces
|
|
60
40
|
|
|
61
41
|
The tool reports:
|
|
@@ -81,15 +61,15 @@ The workflow files now expose a similarly bounded identity surface:
|
|
|
81
61
|
|
|
82
62
|
`sync` owns only the display name between `ossplate:workflow-name` markers. Trigger logic, jobs, auth, and shell steps remain manual.
|
|
83
63
|
|
|
84
|
-
## First
|
|
64
|
+
## First Run
|
|
85
65
|
|
|
86
66
|
1. Either update `ossplate.toml` directly or use `create` / `init` with identity flags.
|
|
87
67
|
2. Run `cargo run --manifest-path core-rs/Cargo.toml -- sync`.
|
|
88
68
|
3. Run `cargo run --manifest-path core-rs/Cargo.toml -- validate`.
|
|
89
|
-
4. Run the
|
|
69
|
+
4. Run the verification flow from [Testing](./testing.md).
|
|
90
70
|
5. Only then expand product code or publish configuration.
|
|
91
71
|
|
|
92
|
-
## Create
|
|
72
|
+
## Create A New Project
|
|
93
73
|
|
|
94
74
|
To scaffold a fresh target from the current template tree:
|
|
95
75
|
|
|
@@ -107,9 +87,9 @@ cargo run --manifest-path core-rs/Cargo.toml -- create ../my-new-project \
|
|
|
107
87
|
|
|
108
88
|
That copies the curated scaffold payload into the target directory, applies any identity overrides to `ossplate.toml`, then runs `sync` on the new target.
|
|
109
89
|
|
|
110
|
-
The packaged scaffold intentionally excludes wrapper test suites and maintainer-only
|
|
90
|
+
The packaged scaffold intentionally excludes wrapper test suites and maintainer-only utilities. Generated projects get the delivery baseline and operator docs, not the source repo's internal harness.
|
|
111
91
|
|
|
112
|
-
##
|
|
92
|
+
## Adopt An Existing Directory
|
|
113
93
|
|
|
114
94
|
To hydrate an existing directory in place:
|
|
115
95
|
|
|
@@ -122,7 +102,7 @@ cargo run --manifest-path core-rs/Cargo.toml -- init \
|
|
|
122
102
|
|
|
123
103
|
`init` ensures the expected scaffold layout exists, copies any missing scaffold files, applies any requested identity overrides, and then runs `sync` so owned metadata matches `ossplate.toml`.
|
|
124
104
|
|
|
125
|
-
##
|
|
105
|
+
## Identity Flags
|
|
126
106
|
|
|
127
107
|
- `--name`
|
|
128
108
|
- `--description`
|
|
@@ -135,10 +115,7 @@ cargo run --manifest-path core-rs/Cargo.toml -- init \
|
|
|
135
115
|
- `--python-package`
|
|
136
116
|
- `--command`
|
|
137
117
|
|
|
138
|
-
##
|
|
139
|
-
|
|
140
|
-
This tool is trying to optimize for a real delivery baseline:
|
|
118
|
+
## Related Decisions
|
|
141
119
|
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
- future phases can add richer scaffold creation and maintenance without first cleaning up metadata drift
|
|
120
|
+
- [ADR 0002: Sync Owns Bounded Identity Surfaces](./adrs/0002-sync-owns-bounded-identity.md)
|
|
121
|
+
- [ADR 0003: Ship A Curated Scaffold Payload](./adrs/0003-curated-scaffold-payload.md)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
Use this guide when cutting a new `ossplate` release.
|
|
4
|
+
|
|
5
|
+
## Current Registry Setup
|
|
6
|
+
|
|
7
|
+
- PyPI publishes from [`.github/workflows/publish.yml`](../.github/workflows/publish.yml) via GitHub OIDC trusted publishing.
|
|
8
|
+
- crates.io publishes from [`.github/workflows/publish.yml`](../.github/workflows/publish.yml) via OIDC trusted publishing with `CARGO_TOKEN` fallback.
|
|
9
|
+
- npm publishes from [`.github/workflows/publish-npm.yml`](../.github/workflows/publish-npm.yml) via OIDC trusted publishing with `NPM_TOKEN` fallback.
|
|
10
|
+
|
|
11
|
+
## Before Release
|
|
12
|
+
|
|
13
|
+
Run the full gate before creating a release:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
./scripts/verify.sh
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Optional local packaging confidence checks:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cargo package --manifest-path core-rs/Cargo.toml
|
|
23
|
+
cargo publish --manifest-path core-rs/Cargo.toml --dry-run
|
|
24
|
+
cd wrapper-js && npm pack --dry-run
|
|
25
|
+
cd ../wrapper-py && python -m build --wheel
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Versioning
|
|
29
|
+
|
|
30
|
+
Keep the registry versions aligned across:
|
|
31
|
+
|
|
32
|
+
- [`core-rs/Cargo.toml`](../core-rs/Cargo.toml)
|
|
33
|
+
- [`wrapper-js/package.json`](../wrapper-js/package.json)
|
|
34
|
+
- [`wrapper-py/pyproject.toml`](../wrapper-py/pyproject.toml)
|
|
35
|
+
|
|
36
|
+
After updating versions, rerun:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
./scripts/verify.sh
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Release Flow
|
|
43
|
+
|
|
44
|
+
1. Commit the version bump and any release notes.
|
|
45
|
+
2. Push `dev`.
|
|
46
|
+
3. Create a GitHub release that targets `dev`, or manually dispatch the publish workflows.
|
|
47
|
+
4. Monitor:
|
|
48
|
+
- [`.github/workflows/publish.yml`](../.github/workflows/publish.yml)
|
|
49
|
+
- [`.github/workflows/publish-npm.yml`](../.github/workflows/publish-npm.yml)
|
|
50
|
+
|
|
51
|
+
## Rerun Behavior
|
|
52
|
+
|
|
53
|
+
The publish jobs are intentionally rerun-safe.
|
|
54
|
+
|
|
55
|
+
- Cargo checks whether the crate version already exists before attempting publish.
|
|
56
|
+
- npm checks whether the package version already exists before attempting publish.
|
|
57
|
+
- PyPI uses `skip-existing: true`.
|
|
58
|
+
|
|
59
|
+
So a second run for the same version should usually succeed by skipping work rather than failing destructively.
|
|
60
|
+
|
|
61
|
+
## Current Published Names
|
|
62
|
+
|
|
63
|
+
- crates.io: `ossplate`
|
|
64
|
+
- npm: `ossplate`
|
|
65
|
+
- PyPI: `ossplate`
|
|
66
|
+
- CLI: `ossplate`
|
|
67
|
+
|
|
68
|
+
## Maintenance
|
|
69
|
+
|
|
70
|
+
- Refresh GitHub Actions dependencies before the Node 20 runner deprecation becomes a problem.
|
|
71
|
+
- Keep the release auth docs aligned with real registry configuration whenever trusted publishing or token fallback changes.
|
package/scaffold/docs/testing.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Testing
|
|
1
|
+
# Testing
|
|
2
2
|
|
|
3
3
|
`ossplate` uses layered verification so the scaffold stays usable as both a source checkout and an installed wrapper distribution.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Layers
|
|
6
6
|
|
|
7
7
|
### Smoke
|
|
8
8
|
|
|
@@ -39,7 +39,7 @@ Artifact assertions are part of the required packaging layer:
|
|
|
39
39
|
- Python wheel content must include the curated scaffold files from `scaffold-manifest.json`
|
|
40
40
|
- Python wheel content must exclude wrapper test files and repo-only validation scripts
|
|
41
41
|
|
|
42
|
-
##
|
|
42
|
+
## Default Local Flow
|
|
43
43
|
|
|
44
44
|
Default path:
|
|
45
45
|
|
|
@@ -59,7 +59,7 @@ Underlying command order:
|
|
|
59
59
|
8. `PYTHONPATH=src python3 -m unittest discover -s tests -p 'test_*.py'`
|
|
60
60
|
9. `python -m build --wheel`
|
|
61
61
|
|
|
62
|
-
## CI
|
|
62
|
+
## CI
|
|
63
63
|
|
|
64
64
|
CI currently enforces:
|
|
65
65
|
|
|
@@ -68,6 +68,6 @@ CI currently enforces:
|
|
|
68
68
|
- JS build, tests, and package dry-run
|
|
69
69
|
- Python tests and wheel build
|
|
70
70
|
|
|
71
|
-
The current artifact tests are the required release-confidence floor.
|
|
71
|
+
The current artifact tests are the required release-confidence floor.
|
|
72
72
|
|
|
73
73
|
For release-specific operator steps, version bumps, and rerun-safe publish expectations, see [`docs/releases.md`](./releases.md).
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Ossplate
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png" alt="Ossplate armor" width="320">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
`ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
|
|
4
8
|
|
|
5
9
|
Use it to:
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ossplate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "ossplate",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.3",
|
|
10
10
|
"license": "Unlicense",
|
|
11
11
|
"bin": {
|
|
12
12
|
"ossplate": "bin/ossplate.js"
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Ossplate
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/stefdevscore/ossplate/dev/assets/illustrations/armour05platemail.png" alt="Ossplate armor" width="320">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
`ossplate` helps you start and maintain a project that ships the same CLI through Rust, npm, and PyPI.
|
|
4
8
|
|
|
5
9
|
Use it to:
|
|
Binary file
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 420" role="img" aria-labelledby="title desc">
|
|
2
|
-
<title id="title">Ossplate chestplate illustration</title>
|
|
3
|
-
<desc id="desc">A bronze chestplate with shoulder guards on a warm background.</desc>
|
|
4
|
-
<defs>
|
|
5
|
-
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
6
|
-
<stop offset="0%" stop-color="#f7ecdd"/>
|
|
7
|
-
<stop offset="100%" stop-color="#dcb88b"/>
|
|
8
|
-
</linearGradient>
|
|
9
|
-
<linearGradient id="metal" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
10
|
-
<stop offset="0%" stop-color="#ddaf68"/>
|
|
11
|
-
<stop offset="50%" stop-color="#b77735"/>
|
|
12
|
-
<stop offset="100%" stop-color="#7b491c"/>
|
|
13
|
-
</linearGradient>
|
|
14
|
-
<linearGradient id="rim" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
15
|
-
<stop offset="0%" stop-color="#fff0bf"/>
|
|
16
|
-
<stop offset="100%" stop-color="#a05d24"/>
|
|
17
|
-
</linearGradient>
|
|
18
|
-
<linearGradient id="highlight" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
19
|
-
<stop offset="0%" stop-color="#fff8de" stop-opacity="0.95"/>
|
|
20
|
-
<stop offset="100%" stop-color="#fff8de" stop-opacity="0"/>
|
|
21
|
-
</linearGradient>
|
|
22
|
-
<filter id="shadow" x="-20%" y="-20%" width="140%" height="160%">
|
|
23
|
-
<feDropShadow dx="0" dy="16" stdDeviation="12" flood-color="#673d18" flood-opacity="0.28"/>
|
|
24
|
-
</filter>
|
|
25
|
-
</defs>
|
|
26
|
-
|
|
27
|
-
<rect width="720" height="420" fill="url(#bg)"/>
|
|
28
|
-
<circle cx="110" cy="84" r="58" fill="#fff7de" opacity="0.4"/>
|
|
29
|
-
<circle cx="607" cy="314" r="76" fill="#a86e33" opacity="0.12"/>
|
|
30
|
-
|
|
31
|
-
<g filter="url(#shadow)" transform="translate(150 28)">
|
|
32
|
-
<path d="M151 46c12-18 27-29 59-34 16-2 42-3 59-3s43 1 59 3c32 5 47 16 59 34l24 35c7 10 4 24-6 31l-22 16c-8 6-19 7-29 2l-27-13v90c0 60-26 102-58 129-32 26-64 39-91 45-27-6-59-19-91-45-32-27-58-69-58-129v-90l-27 13c-10 5-21 4-29-2l-22-16c-10-7-13-21-6-31z" fill="url(#metal)"/>
|
|
33
|
-
<path d="M151 46c12-18 27-29 59-34 16-2 42-3 59-3s43 1 59 3c32 5 47 16 59 34l24 35c7 10 4 24-6 31l-22 16c-8 6-19 7-29 2l-27-13v90c0 60-26 102-58 129-32 26-64 39-91 45-27-6-59-19-91-45-32-27-58-69-58-129v-90l-27 13c-10 5-21 4-29-2l-22-16c-10-7-13-21-6-31z" fill="none" stroke="#6b4118" stroke-width="10" stroke-linejoin="round"/>
|
|
34
|
-
<path d="M210 52c11-14 26-20 44-22 15-2 29-2 46-2s31 0 46 2c18 2 33 8 44 22l15 20c-41 18-92 27-105 29-13-2-64-11-105-29z" fill="#c78741" opacity="0.88"/>
|
|
35
|
-
<path d="M126 74l32 16c10 5 21 4 30-2l20-13c18 9 43 16 72 20v204c-56-15-121-61-121-158V93c0-8-12-13-33-19z" fill="#845022" opacity="0.34"/>
|
|
36
|
-
<path d="M474 74l-32 16c-10 5-21 4-30-2l-20-13c-18 9-43 16-72 20v204c56-15 121-61 121-158V93c0-8 12-13 33-19z" fill="#f0c27a" opacity="0.2"/>
|
|
37
|
-
<path d="M300 92v214" stroke="#6b4118" stroke-width="8" stroke-linecap="round" opacity="0.82"/>
|
|
38
|
-
<path d="M196 134c29 12 65 18 104 18s75-6 104-18" stroke="#6b4118" stroke-width="7" stroke-linecap="round" opacity="0.72"/>
|
|
39
|
-
<path d="M206 215c24 9 56 14 94 14s70-5 94-14" stroke="#6b4118" stroke-width="6" stroke-linecap="round" opacity="0.62"/>
|
|
40
|
-
<path d="M235 42c19 10 40 16 65 19-13 10-23 24-29 41-25-4-48-12-68-25 7-16 17-27 32-35z" fill="url(#highlight)" opacity="0.72"/>
|
|
41
|
-
<path d="M300 61c25-3 46-9 65-19 15 8 25 19 32 35-20 13-43 21-68 25-6-17-16-31-29-41z" fill="url(#highlight)" opacity="0.5"/>
|
|
42
|
-
<path d="M147 62l-41 58c-5 7-3 17 4 22l18 13c5 4 12 4 18 1l41-19-9-53c-2-11-14-21-31-22z" fill="url(#rim)" opacity="0.95"/>
|
|
43
|
-
<path d="M453 62l41 58c5 7 3 17-4 22l-18 13c-5 4-12 4-18 1l-41-19 9-53c2-11 14-21 31-22z" fill="url(#rim)" opacity="0.95"/>
|
|
44
|
-
<path d="M214 310c26 14 56 23 86 28 30-5 60-14 86-28" stroke="#f3d39a" stroke-width="6" stroke-linecap="round" opacity="0.45"/>
|
|
45
|
-
</g>
|
|
46
|
-
</svg>
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# Phase 1 Contract
|
|
2
|
-
|
|
3
|
-
Phase 1 establishes a single canonical CLI surface in the Rust core. The JavaScript and Python packages are wrappers that delegate to that binary rather than implementing separate behavior.
|
|
4
|
-
|
|
5
|
-
## Canonical Commands
|
|
6
|
-
|
|
7
|
-
- `version`
|
|
8
|
-
returns JSON with the tool identity and package version
|
|
9
|
-
- `validate [--path <dir>] [--json]`
|
|
10
|
-
returns validation results for owned metadata surfaces
|
|
11
|
-
- `sync [--path <dir>] [--check]`
|
|
12
|
-
rewrites or checks owned metadata surfaces against the canonical project identity
|
|
13
|
-
- `create <target>`
|
|
14
|
-
copies the curated scaffold payload into a clean target directory, applies optional identity overrides, and synchronizes the owned metadata surfaces there
|
|
15
|
-
- `init [--path <dir>]`
|
|
16
|
-
hydrates an existing directory with the expected scaffold layout, applies optional identity overrides, and then synchronizes owned metadata in place
|
|
17
|
-
|
|
18
|
-
Both commands are available from source checkouts and installed wrapper artifacts that include the staged scaffold payload.
|
|
19
|
-
|
|
20
|
-
## Expected Output Shapes
|
|
21
|
-
|
|
22
|
-
```json
|
|
23
|
-
{"tool":"ossplate","version":"0.1.0"}
|
|
24
|
-
{"ok":true,"issues":[]}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Wrapper Model
|
|
28
|
-
|
|
29
|
-
- Rust is the source of truth for command parsing and output.
|
|
30
|
-
- JavaScript and Python are adapter layers that resolve a packaged binary and forward arguments unchanged.
|
|
31
|
-
- Wrappers preserve stdout, stderr, and exit status from the core binary.
|
|
32
|
-
|
|
33
|
-
## Local Development Override
|
|
34
|
-
|
|
35
|
-
Both wrappers support `OSSPLATE_BINARY` to bypass packaged binary lookup during local development and parity testing.
|
|
36
|
-
|
|
37
|
-
`create` and `init` also honor `OSSPLATE_TEMPLATE_ROOT` when the template source needs to be overridden explicitly.
|
|
38
|
-
|
|
39
|
-
## Packaged Binary Layout
|
|
40
|
-
|
|
41
|
-
Wrappers expect binaries under:
|
|
42
|
-
|
|
43
|
-
```text
|
|
44
|
-
bin/<target>/<executable>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Supported target identifiers are aligned across wrappers:
|
|
48
|
-
|
|
49
|
-
- `darwin-arm64`
|
|
50
|
-
- `darwin-x64`
|
|
51
|
-
- `linux-x64`
|
|
52
|
-
- `win32-x64`
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# Ossplate Roadmap
|
|
2
|
-
|
|
3
|
-
## Current State
|
|
4
|
-
|
|
5
|
-
`ossplate` is no longer a placeholder scaffold. It now ships as a real multi-registry tool with:
|
|
6
|
-
|
|
7
|
-
- a canonical Rust CLI
|
|
8
|
-
- thin npm and Python wrappers
|
|
9
|
-
- real `validate`, `sync`, `create`, and `init` commands
|
|
10
|
-
- curated scaffold payloads in installed wrapper artifacts
|
|
11
|
-
- CI quality gates across Rust, JS, and Python
|
|
12
|
-
- OIDC-first publishing for PyPI, Cargo, and npm, with token fallbacks where configured
|
|
13
|
-
|
|
14
|
-
Published names are aligned as:
|
|
15
|
-
|
|
16
|
-
- crates.io: `ossplate`
|
|
17
|
-
- npm: `ossplate`
|
|
18
|
-
- PyPI: `ossplate`
|
|
19
|
-
- CLI: `ossplate`
|
|
20
|
-
|
|
21
|
-
## Completed
|
|
22
|
-
|
|
23
|
-
### Foundation
|
|
24
|
-
|
|
25
|
-
- Placeholder identity validation is wired into CI.
|
|
26
|
-
- `ossplate.toml` is the canonical identity source.
|
|
27
|
-
- Root docs, testing docs, customization docs, and release docs now exist.
|
|
28
|
-
|
|
29
|
-
### Product Surface
|
|
30
|
-
|
|
31
|
-
- Rust is the only source of product logic.
|
|
32
|
-
- JS and Python are thin wrappers around the packaged binary.
|
|
33
|
-
- The command surface is now real:
|
|
34
|
-
`version`, `validate`, `sync`, `create`, `init`.
|
|
35
|
-
|
|
36
|
-
### Packaging And Artifact Reality
|
|
37
|
-
|
|
38
|
-
- Installed npm and Python artifacts carry the scaffold payload required by `create` and `init`.
|
|
39
|
-
- Scaffold payload staging is curated by manifest rather than broad repo-copy behavior.
|
|
40
|
-
- Artifact tests verify required content and exclude known non-shipping content.
|
|
41
|
-
|
|
42
|
-
### Quality Gates
|
|
43
|
-
|
|
44
|
-
- CI enforces Rust format, clippy, and tests.
|
|
45
|
-
- CI enforces JS build, tests, and package dry-run.
|
|
46
|
-
- CI enforces Python tests and wheel build.
|
|
47
|
-
- Wrapper parity and installed-artifact smoke paths are exercised.
|
|
48
|
-
- `./scripts/verify.sh` mirrors the local gate.
|
|
49
|
-
|
|
50
|
-
### Publishing
|
|
51
|
-
|
|
52
|
-
- Publish flows are rerun-safe.
|
|
53
|
-
- PyPI uses OIDC.
|
|
54
|
-
- Cargo uses OIDC with `CARGO_TOKEN` fallback.
|
|
55
|
-
- npm uses OIDC with `NPM_TOKEN` fallback.
|
|
56
|
-
- The release operator flow is documented in [`docs/releases.md`](./releases.md).
|
|
57
|
-
|
|
58
|
-
## Remaining Priorities
|
|
59
|
-
|
|
60
|
-
## P1
|
|
61
|
-
|
|
62
|
-
- Expand `sync` ownership carefully into a few more identity-only surfaces where bounded ownership is safe.
|
|
63
|
-
- Improve `validate` and `sync --check` output further if field-level drift becomes hard to read at scale.
|
|
64
|
-
- Add a short architecture note describing the current boundary:
|
|
65
|
-
Rust core as product logic, JS/Python as adapters, scaffold payload as distribution asset.
|
|
66
|
-
|
|
67
|
-
## P2
|
|
68
|
-
|
|
69
|
-
- Add optional guidance for generated projects that need browser-based live end-to-end tests.
|
|
70
|
-
- Add examples of failure triage for packaging, publish, and parity regressions.
|
|
71
|
-
- Tighten workflow/docs ownership boundaries further if more fields become sync-managed.
|
|
72
|
-
|
|
73
|
-
## P3
|
|
74
|
-
|
|
75
|
-
- Add architecture-shell guidance for teams that want to scale generated projects into a fuller hexagonal layout.
|
|
76
|
-
- Add ADR guidance if the scaffold evolves into a broader product platform.
|
|
77
|
-
|
|
78
|
-
## Release Maintenance
|
|
79
|
-
|
|
80
|
-
- Refresh GitHub Actions dependencies ahead of the Node 20 runner deprecation.
|
|
81
|
-
- Keep trusted publishing and token fallback documentation aligned with real registry configuration.
|
|
82
|
-
- Keep release-version bumps aligned across Cargo, npm, and PyPI metadata.
|
|
83
|
-
|
|
84
|
-
## Suggested Next Work
|
|
85
|
-
|
|
86
|
-
1. Decide whether `sync` should own any additional identity-only fields beyond the current bounded set.
|
|
87
|
-
2. Improve `validate` and `sync --check` if larger repos need richer drift output.
|
|
88
|
-
3. Add optional browser-e2e guidance for generated projects that include a UI.
|