Skip to content

Commit 7139b7a

Browse files
critesjoshclaude
andcommitted
Add include_code remark plugin for auto-synced ONBOARDING.md snippets
ONBOARDING.md code snippets were manually copied from source files with hardcoded line numbers that went stale on every contract change. This adds the include_code remark plugin to extract snippets from source at build time. - Add docs:start/docs:end markers to 7 source files (19 marker pairs) - Create docs/ONBOARDING.src.md with #include_code directives - Install include_code, remark, remark-cli; add .remarkrc.mjs config - Add yarn docs:build script to regenerate ONBOARDING.md - Update CLAUDE.md with new docs workflow instructions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 19e4c73 commit 7139b7a

14 files changed

Lines changed: 2309 additions & 156 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ log/
77
codegenCache.json
88
store/
99
.tsbuildinfo
10-
.env
10+
.env
11+
.include-code-cache/

.remarkrc.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { remarkIncludeCode } from 'include_code';
2+
3+
export default {
4+
plugins: [
5+
[remarkIncludeCode, {
6+
codeDir: './src',
7+
repository: { owner: 'AztecProtocol', name: 'aztec-starter' },
8+
commitTag: 'main',
9+
validation: 'error',
10+
}]
11+
]
12+
};

CLAUDE.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,27 @@ When updating the Aztec version, update all of these locations:
148148

149149
## ONBOARDING.md Maintenance
150150

151-
This repo includes an `ONBOARDING.md` that serves as a progressive tutorial for Ethereum developers learning Aztec. **When making code changes, check if `ONBOARDING.md` references the changed code** (line numbers, function signatures, code snippets, struct definitions) and update it accordingly. Key sections that embed code:
151+
`ONBOARDING.md` is **generated** — do not edit it directly. Edit `docs/ONBOARDING.src.md` instead, then rebuild:
152152

153-
- Phase 1 (1.3-1.6): Contract storage, public/private functions, game flow table
154-
- Phase 2 (2.3): Noir test patterns and helpers
155-
- Phase 5 (5.1-5.3): Guided exercises with code examples
156-
- Appendix A: File map table
157-
- Appendix B: Commands table
153+
```bash
154+
yarn docs:build # remark docs/ONBOARDING.src.md -o ONBOARDING.md
155+
```
156+
157+
The source file uses `#include_code` directives (via the `include_code` remark plugin) to extract code snippets from source files at build time. Source files have `// docs:start:<name>` / `// docs:end:<name>` marker pairs that define snippet boundaries.
158+
159+
**When making code changes:**
160+
161+
1. Source code snippets update automatically on rebuild — no manual copy needed
162+
2. If you add/remove/rename a marker, update the corresponding `#include_code` directive in `docs/ONBOARDING.src.md`
163+
3. Run `yarn docs:build` to regenerate `ONBOARDING.md`
164+
4. Phase 5 exercises and non-source prose still need manual updates in `docs/ONBOARDING.src.md`
165+
166+
**Files with `docs:start`/`docs:end` markers:**
167+
168+
- `src/main.nr``storage`, `constructor`, `create-game`, `join-game`, `play-round`, `validate-and-play-round`, `finish-game`, `validate-finish-game`, `finalize-game`
169+
- `src/race.nr``race-struct`, `calculate-winner`
170+
- `src/game_round_note.nr``game-round-note`, `game-round-note-new`
171+
- `src/test/utils.nr``test-setup`
172+
- `src/test/helpers.nr``allocation-strategies`, `setup-helpers`
173+
- `src/test/pod_racing.nr``test-initializer`, `test-fail-too-many-points`
174+
- `src/utils/sponsored_fpc.ts``get-sponsored-fpc`

ONBOARDING.md

Lines changed: 319 additions & 134 deletions
Large diffs are not rendered by default.

docs/ONBOARDING.src.md

Lines changed: 857 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"test::devnet": "AZTEC_ENV=devnet yarn test:js",
3333
"test:js": "rm -rf store/pxe && NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --runInBand --config jest.integration.config.json",
3434
"test:nr": "aztec test",
35+
"docs:build": "remark docs/ONBOARDING.src.md -o ONBOARDING.md",
3536
"update-readme-version": "node ./.github/scripts/update-readme-version.js"
3637
},
3738
"dependencies": {
@@ -51,7 +52,10 @@
5152
"@types/jest": "^29.5.11",
5253
"@types/mocha": "^10.0.6",
5354
"@types/node": "^22.15.1",
55+
"include_code": "^0.1.0",
5456
"jest": "^29.7.0",
57+
"remark": "^15.0.1",
58+
"remark-cli": "^12.0.1",
5559
"ts-jest": "^29.1.1",
5660
"ts-node": "^10.9.2",
5761
"typescript": "^5.4.4"

src/game_round_note.nr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use aztec::{macros::notes::note, protocol::{traits::Packable, address::AztecAddress}};
22

3+
// docs:start:game-round-note
34
// GameRoundNote is a private note that stores a player's point allocation for one round
45
// These notes remain private until the player calls finish_game to reveal their totals
56
//
@@ -25,8 +26,10 @@ pub struct GameRoundNote {
2526
// The player who created this note (only they can read it)
2627
pub owner: AztecAddress,
2728
}
29+
// docs:end:game-round-note
2830

2931
impl GameRoundNote {
32+
// docs:start:game-round-note-new
3033
// Creates a new note with the player's round choices
3134
// This note gets stored privately and can only be read by the owner
3235
pub fn new(track1: u8, track2: u8, track3: u8, track4: u8, track5: u8, round: u8, owner: AztecAddress) -> Self {
@@ -40,6 +43,7 @@ impl GameRoundNote {
4043
owner,
4144
}
4245
}
46+
// docs:end:game-round-note-new
4347

4448
// Helper method to access the note data
4549
pub fn get(self) -> Self {

src/main.nr

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub contract PodRacing {
3737
global TOTAL_ROUNDS: u8 = 3; // Each game consists of 3 rounds
3838
global GAME_LENGTH: u32 = 300; // Games expire after 300 blocks
3939

40+
// docs:start:storage
4041
#[storage]
4142
struct Storage<Context> {
4243
// Contract administrator address
@@ -55,14 +56,18 @@ pub contract PodRacing {
5556
// Public leaderboard tracking career victories
5657
win_history: Map<AztecAddress, PublicMutable<u64, Context>, Context>,
5758
}
59+
// docs:end:storage
5860

61+
// docs:start:constructor
5962
#[external("public")]
6063
#[initializer]
6164
fn constructor(admin: AztecAddress) {
6265
debug_log_format("Initializing PodRacing contract with admin {0}", [admin.to_field()]);
6366
self.storage.admin.write(admin);
6467
}
68+
// docs:end:constructor
6569

70+
// docs:start:create-game
6671
// Creates a new game instance
6772
// The caller becomes player1 and waits for an opponent to join
6873
// Sets the game expiration to current block + GAME_LENGTH
@@ -85,7 +90,9 @@ pub contract PodRacing {
8590
);
8691
self.storage.races.at(game_id).write(game);
8792
}
93+
// docs:end:create-game
8894

95+
// docs:start:join-game
8996
// Allows a second player to join an existing game
9097
// After joining, both players can start playing rounds
9198
#[external("public")]
@@ -99,7 +106,9 @@ pub contract PodRacing {
99106
let joined_game = maybe_existing_game.join(player2);
100107
self.storage.races.at(game_id).write(joined_game);
101108
}
109+
// docs:end:join-game
102110

111+
// docs:start:play-round
103112
// Plays a single round by allocating points across 5 tracks
104113
// This is a PRIVATE function - the point allocation remains hidden from the opponent
105114
// Players must play rounds sequentially (round 1, then 2, then 3)
@@ -148,7 +157,9 @@ pub contract PodRacing {
148157
round,
149158
));
150159
}
160+
// docs:end:play-round
151161

162+
// docs:start:validate-and-play-round
152163
// Internal public function to validate and record that a player completed a round
153164
// Updates the public game state to track which round each player is on
154165
// Does NOT reveal the point allocation (that remains private)
@@ -163,7 +174,9 @@ pub contract PodRacing {
163174
// Increment the player's round counter (validates sequential play)
164175
self.storage.races.at(game_id).write(game_in_progress.increment_player_round(player, round));
165176
}
177+
// docs:end:validate-and-play-round
166178

179+
// docs:start:finish-game
167180
// Called after all rounds are complete to reveal a player's total scores
168181
// This is PRIVATE - only the caller can read their own GameRoundNotes
169182
// The function sums up all round allocations per track and publishes totals
@@ -214,7 +227,9 @@ pub contract PodRacing {
214227
total_track5,
215228
));
216229
}
230+
// docs:end:finish-game
217231

232+
// docs:start:validate-finish-game
218233
// Internal public function to store a player's revealed track totals
219234
// Validates that the player hasn't already revealed their scores (all must be 0)
220235
// After both players call finish_game, all scores are public and can be compared
@@ -249,7 +264,9 @@ pub contract PodRacing {
249264
total_track5,
250265
));
251266
}
267+
// docs:end:validate-finish-game
252268

269+
// docs:start:finalize-game
253270
// Determines the winner after both players have revealed their scores
254271
// Can only be called after the game's end_block (time limit expired)
255272
// Compares track totals and declares the player who won more tracks as winner
@@ -276,6 +293,7 @@ pub contract PodRacing {
276293
);
277294
self.storage.win_history.at(winner).write(previous_wins + 1);
278295
}
296+
// docs:end:finalize-game
279297

280298
// Utility function: runs client-side in the PXE, not on-chain.
281299
// Reads the public game state and logs it via debug_log_format.

src/race.nr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use ::aztec::protocol::{
44
traits::{Deserialize, Serialize, Packable, ToField},
55
};
66

7+
// docs:start:race-struct
78
// Race struct stores the public state of a game
89
// This data is visible to everyone and tracks game progress
910
#[derive(Deserialize, Serialize, Eq, Packable)]
@@ -37,6 +38,7 @@ pub struct Race {
3738
// Block number when the game expires (for timeout enforcement)
3839
pub end_block: u32,
3940
}
41+
// docs:end:race-struct
4042

4143
impl Race {
4244
// Creates a new game with player1 set, waiting for player2 to join
@@ -250,6 +252,7 @@ impl Race {
250252
debug_log_format("End block: {0}", [self.end_block as Field]);
251253
}
252254

255+
// docs:start:calculate-winner
253256
// Determines the game winner by comparing track scores
254257
// Winner is whoever won more tracks (best of 5)
255258
//
@@ -305,4 +308,5 @@ impl Race {
305308
self.player2
306309
}
307310
}
311+
// docs:end:calculate-winner
308312
}

src/test/helpers.nr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub global TEST_GAME_ID_8: Field = 8;
1515
pub global TEST_GAME_ID_9: Field = 9;
1616
pub global TEST_GAME_ID_10: Field = 10;
1717

18+
// docs:start:allocation-strategies
1819
// Common point allocations for testing
1920
// Balanced strategy: distribute points evenly
2021
pub unconstrained fn balanced_allocation() -> (u8, u8, u8, u8, u8) {
@@ -35,7 +36,9 @@ pub unconstrained fn defensive_allocation() -> (u8, u8, u8, u8, u8) {
3536
pub unconstrained fn max_allocation() -> (u8, u8, u8, u8, u8) {
3637
(5, 2, 1, 1, 0)
3738
}
39+
// docs:end:allocation-strategies
3840

41+
// docs:start:setup-helpers
3942
// Helper to setup a game with two players
4043
pub unconstrained fn setup_two_player_game(
4144
env: &mut TestEnvironment,
@@ -77,3 +80,4 @@ pub unconstrained fn play_all_rounds_with_strategy(
7780
play_round_with_allocation(env, contract_address, player, game_id, round, allocations[i]);
7881
}
7982
}
83+
// docs:end:setup-helpers

0 commit comments

Comments
 (0)