Skip to content

dev: add fuzzing profiles - #328

Open
DecimalTurn wants to merge 17 commits into
latestfrom
dev-fuzz5
Open

DecimalTurn wants to merge 17 commits into
latestfrom
dev-fuzz5

Conversation

@DecimalTurn

@DecimalTurn DecimalTurn commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Summary

Add composable fuzz profiles for patch() and use structure-first generation to cover AOT layouts that the general TOML generator rarely produces. The new profiles exposed nested AOT formatting and mixed-array append cases, which this PR also fixes.

Changes

  • Refactor the fuzz runner into a shared lifecycle for source generation, mutation, formatting and comparison. Keep the existing numbered variants and their seed behavior.
  • Add the aot, aot-format, aot-general and general-format profiles, plus a structure-first AOT generator and runner.
  • Add seed inspection and failure distillation tools. Generated regression tests now resolve imports from the output file, preserve nested Date values, replay key deletion, and snapshot recorded mutation plans.
  • Fix patching of mixed AOT arrays when appending primitive values, and preserve nested table and AOT section formatting when rebuilding entries unless the caller explicitly selects inline-table formatting.
  • Add regression coverage for nested AOT ownership, including the seed 112 case and document the fuzzing profiles and workflow.

Copilot AI balanced review requested due to automatic review settings October 18, 2026 01:05
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Benchmark comparison

This branch measured against latest in one run on the same runner.
Ratios close to 1.0 are within run to run noise.

Fixture Benchmark Results

Time per iteration. Lower is better.

Parse, smol-toml spec example (toml-spec-example.toml)

Library Performance Slowdown Ratio
🥇 smol-toml@1.9.1 (Date) 9.94 µs/iter 1x
🥈 smol-toml@1.9.1 (Temporal) 15.20 µs/iter 1.53x
🥉 @iarna/toml@3.0.0 28.43 µs/iter 2.86x
4 @decimalturn/toml-patch@3.3.1 (baseline) 45.76 µs/iter 4.60x
5 @decimalturn/toml-patch@3.3.1 (Date, current) 46.15 µs/iter 4.64x 0.99
6 @decimalturn/toml-patch@3.3.1 (Temporal, current) 63.45 µs/iter 6.38x

Parse, smol-toml 5MB mixed (5mb-mixed.toml)

Library Performance Slowdown Ratio
🥇 smol-toml@1.9.1 (Date) 88.89 ms/iter 1x
🥈 smol-toml@1.9.1 (Temporal) 256.66 ms/iter 2.89x
🥉 @decimalturn/toml-patch@3.3.1 (Date, current) 444.41 ms/iter 5x 1.01
4 @decimalturn/toml-patch@3.3.1 (baseline) 447.48 ms/iter 5.03x
5 @decimalturn/toml-patch@3.3.1 (Temporal, current) 719.65 ms/iter 8.10x
- @iarna/toml@3.0.0 DNF DNF

Parse, iarna spec example v0.4.0 (0A-spec-01-example-v0.4.0.toml)

Library Performance Slowdown Ratio
🥇 smol-toml@1.9.1 (Date) 61.43 µs/iter 1x
🥈 smol-toml@1.9.1 (Temporal) 78.85 µs/iter 1.28x
🥉 @iarna/toml@3.0.0 177.56 µs/iter 2.89x
4 @decimalturn/toml-patch@3.3.1 (baseline) 255.93 µs/iter 4.17x
5 @decimalturn/toml-patch@3.3.1 (Date, current) 256.05 µs/iter 4.17x 1.00
6 @decimalturn/toml-patch@3.3.1 (Temporal, current) 297.51 µs/iter 4.84x

Stringify, smol-toml spec example (toml-spec-example.toml)

Library Performance Slowdown Ratio
🥇 smol-toml@1.9.1 7.72 µs/iter 1x
🥈 @iarna/toml@3.0.0 24.92 µs/iter 3.23x
🥉 @decimalturn/toml-patch@3.3.1 (current) 135.85 µs/iter 17.60x 1.08
4 @decimalturn/toml-patch@3.3.1 (baseline) 146.40 µs/iter 18.97x

Stringify, smol-toml 5MB mixed (5mb-mixed.toml)

Library Performance Slowdown Ratio
🥇 smol-toml@1.9.1 98.72 ms/iter 1x
🥈 @iarna/toml@3.0.0 302.47 ms/iter 3.06x
🥉 @decimalturn/toml-patch@3.3.1 (current) 923.33 ms/iter 9.35x 1.07
4 @decimalturn/toml-patch@3.3.1 (baseline) 988.76 ms/iter 10.02x

Stringify, iarna spec example v0.4.0 (0A-spec-01-example-v0.4.0.toml)

Library Performance Slowdown Ratio
🥇 smol-toml@1.9.1 32.77 µs/iter 1x
🥈 @iarna/toml@3.0.0 124.46 µs/iter 3.80x
🥉 @decimalturn/toml-patch@3.3.1 (current) 607.51 µs/iter 18.54x 1.05
4 @decimalturn/toml-patch@3.3.1 (baseline) 636.36 µs/iter 19.42x

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Format inconsistencies and scaffold-generation defects prevent reliable reproduction of fuzz failures.

5 open issues
What changed in this PR

Refactors patch fuzzing into composable profiles and adds focused arrays-of-tables coverage.

Changes:

  • Extracts configurable generation, mutation, formatting and comparison strategies.
  • Adds numbered runners, profile inspection and regression scaffolding.
  • Adds deterministic tests and updates documentation and CI.
File Description
src/​__tests__/​inspect-fuzz-seed.ts Adds profile inspection.
src/​__tests__/​fuzz5.test.ts Tests structural seeds.
src/​__tests__/​fuzz1.test.ts Tests baseline seeds.
src/​__tests__/​fuzz-profiles.ts Defines composable profiles.
src/​__tests__/​fuzz-patch5.ts Adds structural generation and mutations.
src/​__tests__/​fuzz-patch4.ts Migrates date-focused configuration.
src/​__tests__/​fuzz-patch3.ts Migrates layout configuration.
src/​__tests__/​fuzz-patch2.ts Migrates dotted-key configuration.
src/​__tests__/​fuzz-patch1.ts Adds numbered baseline entry point.
src/​__tests__/​fuzz-patch.ts Extracts shared runner and metadata.
src/​__tests__/​fuzz-composable.test.ts Checks composition and determinism.
scripts/​fuzz-run5.ts Adds structural seed sweeps.
scripts/​fuzz-run1.ts Adds numbered baseline sweeps.
scripts/​fuzz-run.ts Removes unnumbered runner.
scripts/​distill-profile.ts Generates regression scaffolds.
package.json Updates fuzz commands.
docs/​PLAN-Composable-Fuzz-Harness.md Records architecture and migration plan.
docs/​Fuzz-Testing.md Documents variants and profiles.
.github/​workflows/​test-and-build.yml Uses numbered baseline command.

Review effort: Balanced

Comment thread scripts/distill-profile.ts Outdated
Comment thread scripts/distill-profile.ts Outdated
Comment thread scripts/distill-profile.ts
Comment thread src/__tests__/fuzz-patch.ts Outdated
Comment thread src/__tests__/fuzz-patch5.ts
@DecimalTurn
DecimalTurn marked this pull request as ready for review October 29, 2026 19:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/patch.ts
Comment on lines +1596 to +1597
for (const entry of original.items.filter(isTableArray)) {
const sections = findAotEntryScopeItems(original, entry).filter(item => isTable(item) || isTableArray(item));
};
}

return result;
Comment on lines +16 to +29
import { runProfile, type FuzzProfile } from './fuzz-profiles';

const requestedProfile = process.argv.includes('--profile')
? process.argv[process.argv.indexOf('--profile') + 1]
: process.argv.includes('--variant') && process.argv[process.argv.indexOf('--variant') + 1] === '5'
? 'aot'
: undefined;

if (requestedProfile) {
const profile = requestedProfile as FuzzProfile;
const mutationIndex = process.argv.indexOf('--mutations');
const profileMutationCount = mutationIndex >= 0 ? Number(process.argv[mutationIndex + 1]) : 3;
const profileSeed = Number(process.argv[2] || '42');
const result = runProfile(profileSeed, profile, profileMutationCount);
Comment thread src/patch.ts
indentWidthExplicit = false,
trailingCommaExplicit = true
trailingCommaExplicit = true,
inlineTableStartExplicit = false
Comment thread src/patch.ts
Comment on lines +1645 to +1646
for (const section of oldSections.reverse()) {
removeMember(original, original, section);
Comment thread src/patch.ts
Comment on lines +1703 to +1705
const tableArrayKey = parent_path.filter((part): part is string => typeof part === 'string');
const aotStyle = aotStylePathsByKey.get(JSON.stringify(tableArrayKey));
const preserveScopedSections = is_table_array && !inlineTableStartExplicit && !!aotStyle;
Comment thread src/patch.ts
const tableArrayKey = parent_path.filter((part): part is string => typeof part === 'string');
const aotStyle = aotStylePathsByKey.get(JSON.stringify(tableArrayKey));
const preserveScopedSections = is_table_array && !inlineTableStartExplicit && !!aotStyle;
const updated_js = toJS(updated.items, '', { temporal });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants