Skip to content

Commit 08dad8c

Browse files
docs: versioning clarity, measured performance guidance, and fail-closed publishing (#7719)
## Summary - Clarify versioning across the README and book index: per-tag published books, the distinction between the docs source version, the Fuelup network channels, and the newest upstream release. - Add measured, evidence-scoped optimization guidance: storage read-once/write-once patterns, benchmarking the executed entrypoint with `forc test --release --locked` and a committed `Forc.lock`, and honest inline-assembly expectations — wins only where asm removes fixed-loop or branch scaffolding, with an isolated typed interface and a readable Sway oracle for differential testing. - Add predicate security-design guidance covering the transaction dimensions a predicate must bind (chain/domain, script, counts, owners/recipients/assets/amounts/net gain, nonces, validity windows, witness content). - Align the cargo-generate Rust test templates on one `fuels` version matched to the SDK harness, enforced by a CI check. - Make documentation publishing fail closed: the gh-pages workflow verifies plugin compatibility tags and only real tag pushes can move the `latest` redirect. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NWti3Mzc7HHsy9EspKHKof --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 47e5e90 commit 08dad8c

19 files changed

Lines changed: 238 additions & 51 deletions

File tree

.github/workflows/gh-pages.yml

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ on:
77
tags:
88
- v*
99
workflow_dispatch:
10+
inputs:
11+
version:
12+
description: "Release tag to backfill (for example, v0.70.2); leave empty to publish master"
13+
required: false
14+
type: string
1015

1116
env:
1217
RUST_VERSION: 1.93.0
@@ -19,6 +24,30 @@ jobs:
1924
runs-on: warp-ubuntu-latest-x64-2x # needs at least 100 gb hdd
2025
steps:
2126
- uses: actions/checkout@v6
27+
with:
28+
ref: ${{ inputs.version || github.ref }}
29+
30+
- name: Resolve documentation target
31+
id: docs_target
32+
env:
33+
REQUESTED_VERSION: ${{ inputs.version }}
34+
run: |
35+
if [[ -n "$REQUESTED_VERSION" ]]; then
36+
checked_out_tag="$(git describe --tags --exact-match)"
37+
if [[ "$checked_out_tag" != "$REQUESTED_VERSION" ]]; then
38+
echo "Requested $REQUESTED_VERSION but checked out $checked_out_tag" >&2
39+
exit 1
40+
fi
41+
echo "tag=$REQUESTED_VERSION" >> "$GITHUB_OUTPUT"
42+
elif [[ "$GITHUB_REF" == refs/tags/* ]]; then
43+
echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
44+
elif [[ "$GITHUB_REF" != refs/heads/master ]]; then
45+
echo "Refusing to publish master documentation from $GITHUB_REF" >&2
46+
echo "Select an exact release tag with the version input instead." >&2
47+
exit 1
48+
else
49+
echo "tag=" >> "$GITHUB_OUTPUT"
50+
fi
2251
2352
- uses: Swatinem/rust-cache@v2
2453

@@ -31,18 +60,40 @@ jobs:
3160
run: cargo install --locked --debug --path ./forc
3261

3362
- name: Install Forc plugins
63+
env:
64+
DOCS_TAG: ${{ steps.docs_target.outputs.tag }}
3465
run: |
3566
cargo uninstall forc-mcp || true
3667
rm -f "${CARGO_HOME:-$HOME/.cargo}/bin/forc-mcp"
37-
cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-client
38-
cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-crypto
39-
cargo install --locked --debug --path ./forc-plugins/forc-debug
40-
cargo install --locked --debug --path ./forc-plugins/forc-fmt
41-
cargo install --locked --debug --path ./forc-plugins/forc-doc
42-
cargo install --locked --debug --path ./forc-plugins/forc-lsp
43-
cargo install --locked --debug --path ./forc-plugins/forc-migrate
44-
cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-node
45-
cargo install --locked --debug --path ./forc-plugins/forc-publish
68+
if [[ -d ./forc-plugins/forc-client ]]; then
69+
# Before the plugin repository split, release tags carried these
70+
# plugins in-tree. Use those exact sources when backfilling docs.
71+
for plugin in forc-client forc-crypto forc-mcp forc-node; do
72+
if [[ -d "./forc-plugins/$plugin" ]]; then
73+
cargo install --locked --debug --path "./forc-plugins/$plugin"
74+
fi
75+
done
76+
elif [[ -z "$DOCS_TAG" ]]; then
77+
# Default-branch documentation follows the current plugin sources.
78+
cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-client
79+
cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-crypto
80+
cargo install --locked --debug --git https://github.com/FuelLabs/forc forc-node
81+
elif [[ "$DOCS_TAG" == v0.71.2 ]]; then
82+
# Post-split plugins release independently. Pin the compatibility
83+
# set recorded for Sway v0.71.2 in FuelLabs/forc releases.toml.
84+
cargo install --locked --debug --git https://github.com/FuelLabs/forc --tag forc-client-0.71.3 forc-client
85+
cargo install --locked --debug --git https://github.com/FuelLabs/forc --tag forc-crypto-0.71.1 forc-crypto
86+
cargo install --locked --debug --git https://github.com/FuelLabs/forc --tag forc-node-0.71.3 forc-node
87+
else
88+
echo "No independent Forc plugin compatibility set is recorded for $DOCS_TAG" >&2
89+
echo "Add exact plugin tags before publishing this release's command reference." >&2
90+
exit 1
91+
fi
92+
for plugin in forc-debug forc-fmt forc-doc forc-lsp forc-migrate forc-publish; do
93+
if [[ -d "./forc-plugins/$plugin" ]]; then
94+
cargo install --locked --debug --path "./forc-plugins/$plugin"
95+
fi
96+
done
4697
4798
- name: Install mdbook-forc-documenter
4899
run: cargo install --locked --debug --path ./scripts/mdbook-forc-documenter
@@ -67,23 +118,23 @@ jobs:
67118
github_token: ${{ secrets.GITHUB_TOKEN }}
68119
publish_dir: ./sway-lib-std/out/doc
69120
destination_dir: master
70-
if: github.ref == 'refs/heads/master'
121+
if: steps.docs_target.outputs.tag == ''
71122

72123
- name: Deploy master book
73124
uses: peaceiris/actions-gh-pages@v4
74125
with:
75126
github_token: ${{ secrets.GITHUB_TOKEN }}
76127
publish_dir: ./docs/book/book
77128
destination_dir: master/book
78-
if: github.ref == 'refs/heads/master'
129+
if: steps.docs_target.outputs.tag == ''
79130

80131
- name: Deploy master reference
81132
uses: peaceiris/actions-gh-pages@v4
82133
with:
83134
github_token: ${{ secrets.GITHUB_TOKEN }}
84135
publish_dir: ./docs/reference/book
85136
destination_dir: master/reference
86-
if: github.ref == 'refs/heads/master'
137+
if: steps.docs_target.outputs.tag == ''
87138

88139
- name: Create master book redirect file
89140
run: |
@@ -94,7 +145,7 @@ jobs:
94145
<meta http-equiv="refresh" content="0; URL=../master/book">
95146
<link rel="canonical" href="../master/book">
96147
EOF
97-
if: github.ref == 'refs/heads/master'
148+
if: steps.docs_target.outputs.tag == ''
98149

99150
- name: Deploy index.html redirect file to master
100151
uses: peaceiris/actions-gh-pages@v4
@@ -103,67 +154,61 @@ jobs:
103154
publish_dir: ./tmp
104155
destination_dir: ./master
105156
keep_files: true
106-
if: github.ref == 'refs/heads/master'
107-
108-
- name: Get tag
109-
id: branch_name
110-
run: |
111-
echo ::set-output name=BRANCH_NAME::${GITHUB_REF#refs/tags/}
112-
if: startsWith(github.ref, 'refs/tags')
157+
if: steps.docs_target.outputs.tag == ''
113158

114159
- name: Deploy book tag
115160
uses: peaceiris/actions-gh-pages@v4
116161
with:
117162
github_token: ${{ secrets.GITHUB_TOKEN }}
118163
publish_dir: ./docs/book/book
119-
destination_dir: ${{ steps.branch_name.outputs.BRANCH_NAME }}/book
120-
if: startsWith(github.ref, 'refs/tags')
164+
destination_dir: ${{ steps.docs_target.outputs.tag }}/book
165+
if: steps.docs_target.outputs.tag != ''
121166

122167
- name: Deploy reference tag
123168
uses: peaceiris/actions-gh-pages@v4
124169
with:
125170
github_token: ${{ secrets.GITHUB_TOKEN }}
126171
publish_dir: ./docs/reference/book
127-
destination_dir: ${{ steps.branch_name.outputs.BRANCH_NAME }}/reference
128-
if: startsWith(github.ref, 'refs/tags')
172+
destination_dir: ${{ steps.docs_target.outputs.tag }}/reference
173+
if: steps.docs_target.outputs.tag != ''
129174

130175
- name: Deploy std tag
131176
uses: peaceiris/actions-gh-pages@v4
132177
with:
133178
github_token: ${{ secrets.GITHUB_TOKEN }}
134179
publish_dir: ./sway-lib-std/out/doc/std
135-
destination_dir: ${{ steps.branch_name.outputs.BRANCH_NAME }}/std
136-
if: startsWith(github.ref, 'refs/tags')
180+
destination_dir: ${{ steps.docs_target.outputs.tag }}/std
181+
if: steps.docs_target.outputs.tag != ''
137182

138183
- name: Create tag book redirect file
139184
run: |
140185
mkdir ./tmp
141186
cat > ./tmp/index.html <<EOF
142187
<!DOCTYPE html>
143188
<meta charset="utf-8">
144-
<meta http-equiv="refresh" content="0; URL=../${{ steps.branch_name.outputs.BRANCH_NAME }}/book">
145-
<link rel="canonical" href="../${{ steps.branch_name.outputs.BRANCH_NAME }}/book">
189+
<meta http-equiv="refresh" content="0; URL=../${{ steps.docs_target.outputs.tag }}/book">
190+
<link rel="canonical" href="../${{ steps.docs_target.outputs.tag }}/book">
146191
EOF
147-
if: startsWith(github.ref, 'refs/tags')
192+
if: steps.docs_target.outputs.tag != ''
148193

149194
- name: Deploy index.html redirect file to tag
150195
uses: peaceiris/actions-gh-pages@v4
151196
with:
152197
github_token: ${{ secrets.GITHUB_TOKEN }}
153198
publish_dir: ./tmp
154-
destination_dir: ./${{ steps.branch_name.outputs.BRANCH_NAME }}
199+
destination_dir: ./${{ steps.docs_target.outputs.tag }}
155200
keep_files: true
156-
if: startsWith(github.ref, 'refs/tags')
201+
if: steps.docs_target.outputs.tag != ''
157202

158203
- name: Create latest HTML redirect file
159-
if: startsWith(github.ref, 'refs/tags')
204+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
160205
run: |
161206
mkdir ./latest
162207
cat > ./latest/index.html <<EOF
163208
<!DOCTYPE html>
164209
<meta charset="utf-8">
165-
<meta http-equiv="refresh" content="0; URL=../${{ steps.branch_name.outputs.BRANCH_NAME }}/book">
166-
<link rel="canonical" href="../${{ steps.branch_name.outputs.BRANCH_NAME }}/book">
210+
<meta http-equiv="refresh" content="0; URL=../${{ steps.docs_target.outputs.tag }}/book">
211+
<link rel="canonical" href="../${{ steps.docs_target.outputs.tag }}/book">
167212
EOF
168213
169214
- name: Set latest to point to tag
@@ -172,4 +217,4 @@ jobs:
172217
github_token: ${{ secrets.GITHUB_TOKEN }}
173218
publish_dir: ./latest/
174219
destination_dir: ./latest/
175-
if: startsWith(github.ref, 'refs/tags')
220+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')

.github/workflows/scripts/check-sdk-harness-version.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,20 @@ get_version() {
1313
echo "$version"
1414
}
1515

16+
if ! command -v toml >/dev/null 2>&1; then
17+
printf "ERROR: toml-cli is required to check SDK dependency versions.\n" >&2
18+
exit 1
19+
fi
20+
1621
IFS=',' read -ra PACKAGES <<< "$PACKAGE_NAMES"
1722
mismatch=0
1823
for PACKAGE in "${PACKAGES[@]}"; do
1924
VERSION_FIRST=$(get_version ./Cargo.toml "workspace.dependencies.$PACKAGE")
2025
VERSION_SECOND=$(get_version ./test/src/sdk-harness/Cargo.toml "dependencies.$PACKAGE")
26+
if [ -z "$VERSION_FIRST" ] || [ -z "$VERSION_SECOND" ]; then
27+
printf "ERROR: Could not read %s from both workspace and SDK harness manifests.\n" "$PACKAGE" >&2
28+
mismatch=1
29+
fi
2130
printf "$PACKAGE\n sway repo: $VERSION_FIRST\n sdk-harness: $VERSION_SECOND\n"
2231
if [ "$VERSION_FIRST" != "$VERSION_SECOND" ]; then
2332
printf "ERROR: Version mismatch for $PACKAGE\n"
@@ -30,3 +39,45 @@ if [ $mismatch -ne 0 ]; then
3039
else
3140
printf "\nAll specified package versions match.\n"
3241
fi
42+
43+
# The public cargo-generate templates must use the same Rust SDK generation as
44+
# the SDK harness tested above. Keep this list explicit so a new program-type
45+
# template must opt into the compatibility check.
46+
FUELS_VERSION=$(get_version ./Cargo.toml "workspace.dependencies.fuels")
47+
TEMPLATES=(
48+
"./templates/sway-predicate-test-rs/template/Cargo.toml"
49+
"./templates/sway-script-test-rs/template/Cargo.toml"
50+
"./templates/sway-test-rs/template/Cargo.toml"
51+
)
52+
53+
template_mismatch=0
54+
mapfile -t DISCOVERED_TEMPLATES < <(
55+
find ./templates -mindepth 3 -maxdepth 3 -type f \
56+
-path '*/template/Cargo.toml' | sort
57+
)
58+
if [ "${DISCOVERED_TEMPLATES[*]}" != "${TEMPLATES[*]}" ]; then
59+
printf "ERROR: Public template manifest list is out of sync.\n" >&2
60+
printf "Expected: %s\n" "${TEMPLATES[*]}" >&2
61+
printf "Found: %s\n" "${DISCOVERED_TEMPLATES[*]}" >&2
62+
template_mismatch=1
63+
fi
64+
65+
for TEMPLATE in "${TEMPLATES[@]}"; do
66+
TEMPLATE_VERSION=$(get_version "$TEMPLATE" "dev-dependencies.fuels")
67+
if [ -z "$FUELS_VERSION" ] || [ -z "$TEMPLATE_VERSION" ]; then
68+
printf "ERROR: Could not read fuels version from workspace or %s\n" "$TEMPLATE" >&2
69+
template_mismatch=1
70+
fi
71+
printf "fuels\n sway repo: $FUELS_VERSION\n $TEMPLATE: $TEMPLATE_VERSION\n"
72+
if [ "$FUELS_VERSION" != "$TEMPLATE_VERSION" ]; then
73+
printf "ERROR: Rust SDK version mismatch for %s\n" "$TEMPLATE"
74+
template_mismatch=1
75+
fi
76+
done
77+
78+
if [ $template_mismatch -ne 0 ]; then
79+
printf "\nRust SDK versions in the public templates must match workspace.dependencies.fuels.\n"
80+
exit 1
81+
else
82+
printf "\nAll public template Rust SDK versions match the Sway SDK harness.\n"
83+
fi

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@ Sway is a language developed for the [Fuel](https://docs.fuel.network/docs/intro
1010

1111
## Documentation
1212

13-
For user documentation, including installing release builds, see the Sway Book: <https://fuellabs.github.io/sway/latest/>.
13+
For user documentation, including installing release builds, see the latest released Sway Book: <https://fuellabs.github.io/sway/latest/>.
1414

15-
For Sway Standard library documentation, see: <https://fuellabs.github.io/sway/master/std/>
15+
The documentation URLs describe different source versions:
1616

17-
Also view the technical reference for the Sway programming language: <https://fuellabs.github.io/sway/master/reference/>
17+
- `latest` redirects to the most recently published Sway release.
18+
- `vX.Y.Z` is documentation built from that exact Sway release tag.
19+
- `master` is built from the default branch and may describe unreleased behavior.
20+
21+
These labels are Sway documentation versions. They are not Fuelup channel names and do not identify the toolchain activated on a Fuel network. Check the compiler you are running with `forc --version` and consult the [Fuelup channel documentation](https://install.fuel.network/master/concepts/channels.html) when selecting network-compatible tooling.
22+
23+
For Sway standard library documentation from the default branch, see <https://fuellabs.github.io/sway/master/std/>.
24+
25+
Also view the default-branch technical reference for the Sway programming language at <https://fuellabs.github.io/sway/master/reference/>.
26+
27+
The **Stable** Sway and Forc pages on `docs.fuel.network` are published by [`FuelLabs/docs-hub`](https://github.com/FuelLabs/docs-hub) from an explicitly selected Sway release. That selection can differ from both the newest upstream release and the compiler in a named Fuelup network channel.
1828

1929
## Building from Source
2030

docs/book/spell-check-custom-words.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,6 @@ decodable
282282
encodable
283283
Vec
284284
hashable
285+
revalidate
286+
revalidated
287+
executable's

docs/book/src/advanced/assembly.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ Note that in the above example:
4646

4747
An important note is that the `ji` and `jnei` opcodes are not available within an `asm` block. For those looking to introduce control flow to `asm` blocks, it is recommended to surround smaller chunks of `asm` with control flow (`if`, `else`, and `while`).
4848

49+
## Optimization Guidance
50+
51+
Inline assembly is not automatically cheaper than Sway. Measurements with Forc `0.71.2` found wins only when assembly removed fixed-loop or branch scaffolding; straight-line arithmetic, and assembly placed inside the original Sway loop, were gas-neutral or regressed.
52+
53+
Compare `forc test --release` gas and release bytecode size independently. Preserve checked-overflow behavior, zero-divisor and shift guards, evaluation order, memory effects, and reserved-register rules. Isolate a measured assembly kernel behind a small typed Sway function, and retain a readable Sway implementation as an oracle to differential-test the assembly against over boundary inputs. Revalidate assembly after every compiler or FuelVM upgrade.
54+
4955
## Helpful Links
5056

5157
For examples of assembly in action, check out the [Sway standard library](https://github.com/FuelLabs/sway/tree/master/sway-lib-std).

docs/book/src/blockchain-development/storage.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ To read a storage variable, you also need to use the `storage` keyword. You may
2929
{{#include ../../../../examples/basic_storage_variables/src/main.sw:basic_storage_read}}
3030
```
3131

32+
## Performance and Safety
33+
34+
The compiler does not always eliminate duplicate storage operations:
35+
36+
- load an invariant value once before a loop and pass it to helpers;
37+
- write only changed state and move invariant writes outside loops;
38+
- do not reuse a cached value across an external call unless the reachable callbacks cannot mutate it;
39+
- benchmark an ABI entry point, because helper-only gas does not price a storage path that the benchmark never executes.
40+
3241
## Storing Structs
3342

3443
To store a struct in storage, each variable must be assigned in the `storage` block. This can be either my assigning the fields individually or using a public [constructor](../basics/methods_and_associated_functions.md#constructors) that can be evaluated to a constant during compilation.

docs/book/src/examples/sway_applications.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
The [Sway-Applications](https://github.com/FuelLabs/sway-applications) Repository contains end-to-end example applications that are written in Sway in order to demonstrate what can be built.
44

5+
> **Legacy examples:** These applications are version-pinned snapshots and are not continuously updated for the current Sway release or Fuel network toolchains. Read the repository's [status and toolchain matrix](https://github.com/FuelLabs/sway-applications#project-status-and-toolchains) before using an example. Treat old syntax, dependencies, transaction construction, and security patterns as historical until revalidated.
6+
57
## Asset Management
68

79
- [Airdrop](https://github.com/FuelLabs/sway-applications/tree/master/airdrop) is an asset distribution program where users are able to claim assets given a valid merkle proof.

docs/book/src/forc/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Forc stands for Fuel Orchestrator. Forc provides a variety of tools and commands for developers working with the Fuel ecosystem, such as scaffolding a new project, formatting, running scripts, deploying contracts, testing contracts, and more. If you're coming from a Rust background, forc is similar to cargo.
44

5+
The core `forc` executable is released from the [`FuelLabs/sway`](https://github.com/FuelLabs/sway) repository. Some network-facing plugins, including `forc-client` and `forc-node`, are released independently from [`FuelLabs/forc`](https://github.com/FuelLabs/forc). Consequently, a Sway compiler version is not a complete plugin compatibility matrix. Check each installed executable with `<command> --version`; when its version differs from this book's release, prefer that executable's `--help` output for exact flags.
6+
57
If you are new to Forc, see the [Forc Project](https://docs.fuel.network/docs/sway/introduction/forc_project/) introduction section.
68

79
For a comprehensive overview of the Forc CLI commands, see the [Commands](./commands/index.md) section.

docs/book/src/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Welcome to the Sway programming language book 🌴.
44

5+
> **Documentation version:** A `vX.Y.Z` URL is built from that exact Sway release tag. The [`latest`](https://fuellabs.github.io/sway/latest/) URL redirects to the most recently published release, while [`master`](https://fuellabs.github.io/sway/master/book/) describes the default branch and may include unreleased behavior. These are documentation versions, not Fuelup channel names. Run `forc --version` to identify the compiler you are using and consult the [Fuelup channel documentation](https://install.fuel.network/master/concepts/channels.html) before choosing tooling for a network.
6+
57
**Q: Hi! What is Sway?**
68

79
Sway is a domain-specific programming language for implementing smart contracts on blockchain platforms, most notably for the [Fuel Virtual Machine (Fuel VM)](https://docs.fuel.network/docs/specs/fuel-vm/).
@@ -55,7 +57,7 @@ If you don't want to install anything just yet, you can use the [Sway Playground
5557

5658
**Q: Where can I find example Sway code?**
5759

58-
You can find example applications built with Sway in the [Sway Applications repository](https://github.com/FuelLabs/sway-applications) on GitHub. You can also find projects building on Fuel in the [Fuel ecosystem home](https://app.fuel.network/ecosystem).
60+
You can find historical example applications built with Sway in the [Sway Applications repository](https://github.com/FuelLabs/sway-applications) on GitHub. That repository contains version-pinned legacy snapshots, so check its status matrix before copying an example into a current project. You can also find projects building on Fuel in the [Fuel ecosystem home](https://app.fuel.network/ecosystem).
5961

6062
**Q: What is the standard library?**
6163

0 commit comments

Comments
 (0)