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: 7 additions & 1 deletion docs/engineering/coding-style.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Coding Style — hand-written code

Date: 2026-10-16
Date: 2026-10-18

Binding authoring style for every hand-written line in this repository — product code,
`tools/`, and tests. Generated output is governed by the generator's emitters (ADR-0003);
Expand Down Expand Up @@ -126,6 +126,12 @@ for a pile of static steps.
rather than treating `IReadOnly*` as a deep-immutability guarantee. Generated wire models are
shallow init-only DTOs (ADR-0004/0014); hand-written domain types choose stronger ownership only
when a concrete consumer or concurrency boundary requires it.
- **Regular expressions:** a regex whose pattern matches in linear time and whose input is
repository-pinned or test-local passes `Timeout.Infinite` (`matchTimeoutMilliseconds:`), or
`Regex.InfiniteMatchTimeout` where the API takes a `TimeSpan`. A regex over untrusted input, or
one whose pattern can backtrack superlinearly, keeps a finite timeout with its reason beside it.
The match timeout is wall-clock: on a linear pattern over pinned input it guards nothing and fails
on a stalled runner (#426).

## 4. Layout

Expand Down
2 changes: 1 addition & 1 deletion tests/OpenCode.Sdk.Tests/Harness/DriveManifestTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed class DriveManifestTests
/// builds for.
/// </summary>
private static readonly Regex InstanceNamePattern =
new("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$", RegexOptions.None, TimeSpan.FromSeconds(1));
new("^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$", RegexOptions.None, Regex.InfiniteMatchTimeout);

/// <summary>
/// MockFileSystem simulates the host OS, so the registry path must stay portable across
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ private static bool IsHash(string path) =>

private static bool IsScript(string path) => ScriptExtensions.Any(extension => path.EndsWith(extension, StringComparison.Ordinal));

// Quadratic, so it keeps a finite match timeout: each unclosed "<#" scans lazily to the end of the text before the next one retries.
[GeneratedRegex(@"<#.*?#>", RegexOptions.Singleline, 1000)]
private static partial Regex PowerShellBlock();
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,21 +132,23 @@ private static bool HasReason(string rest)
(ResearchLogNumber, ResearchLogPattern()),
];

[GeneratedRegex(@"\bTask [0-9]+\b", RegexOptions.None, 1000)]
[GeneratedRegex(@"\bTask [0-9]+\b", RegexOptions.None, Timeout.Infinite)]
private static partial Regex TaskPattern();

[GeneratedRegex(@"\bdesign §", RegexOptions.None, 1000)]
[GeneratedRegex(@"\bdesign §", RegexOptions.None, Timeout.Infinite)]
private static partial Regex DesignSectionPattern();

// Quadratic, so it keeps a finite match timeout: the path class also matches "docs/", so repeated prefixes without ".md" rescan the tail.
[GeneratedRegex(@"\bdocs/[A-Za-z0-9_./-]*\.md\b", RegexOptions.None, 1000)]
private static partial Regex DocumentationPathPattern();

[GeneratedRegex(@"\bQ[0-9]{3}\b", RegexOptions.None, 1000)]
[GeneratedRegex(@"\bQ[0-9]{3}\b", RegexOptions.None, Timeout.Infinite)]
private static partial Regex ResearchLogPattern();

[GeneratedRegex(@"(?<![A-Za-z])auto_?verify(?![A-Za-z])", RegexOptions.IgnoreCase, 1000)]
[GeneratedRegex(@"(?<![A-Za-z])auto_?verify(?![A-Za-z])", RegexOptions.IgnoreCase, Timeout.Infinite)]
private static partial Regex AutoAcceptWord();

// Quadratic, so it keeps a finite match timeout: each unanchored start of the marker retries the rest of a line that cannot reach the end anchor.
[GeneratedRegex(@"\bStryker\s*disable(?<rest>.*)$", RegexOptions.IgnoreCase, 1000)]
private static partial Regex StrykerDisable();
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ await Assert.That(run.StandardOutputText.Replace("\r\n", "\n", StringComparison.
}

/// <summary>The warning codes the trim analyzer (<c>IL2xxx</c>) and the AOT compiler (<c>IL3xxx</c>) emit.</summary>
[GeneratedRegex(@"\bIL[23]\d{3}\b", RegexOptions.None, 1000)]
[GeneratedRegex(@"\bIL[23]\d{3}\b", RegexOptions.None, Timeout.Infinite)]
private static partial Regex AotWarningCodes();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@ public sealed partial class PackedReadmeTests(PackedConsumerFeed feed)
{
private static readonly RealFileSystem FileSystem = new();

private static readonly TimeSpan MatchTimeout = TimeSpan.FromSeconds(1);

[Test]
[Arguments("OpenCodeDotNet.Sdk")]
[Arguments("OpenCodeDotNet.Sdk.Extensions")]
public async Task The_Packed_Readme_Should_Link_The_Repository_At_The_Packed_Commit(string packageId)
{
var package = await ReadPackageAsync(packageId);
var link = new Regex(Regex.Escape(package.RepositoryUrl) + @"/(?:blob|tree)/(?<ref>[^/)\s]+)/", RegexOptions.None, MatchTimeout);
var link = new Regex(Regex.Escape(package.RepositoryUrl) + @"/(?:blob|tree)/(?<ref>[^/)\s]+)/", RegexOptions.None, Regex.InfiniteMatchTimeout);
var references = link.Matches(package.Readme).Select(static match => match.Groups["ref"].Value).ToArray();

await Assert.That(package.Commit).Matches(CommitPattern());
Expand Down Expand Up @@ -92,10 +90,10 @@ private static async Task<string> ReadEntryAsync(ZipArchive archive, string name
}
}

[GeneratedRegex("^[0-9a-f]{40}$", RegexOptions.None, 1000)]
[GeneratedRegex("^[0-9a-f]{40}$", RegexOptions.None, Timeout.Infinite)]
private static partial Regex CommitPattern();

[GeneratedRegex("%[0-9A-Fa-f]{2}", RegexOptions.None, 1000)]
[GeneratedRegex("%[0-9A-Fa-f]{2}", RegexOptions.None, Timeout.Infinite)]
private static partial Regex PercentSequence();

private sealed record PackedReadme(string Readme, string RepositoryUrl, string Commit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,13 @@ private static bool IsLowSeverity(string value) =>

private static bool Is(string actual, string expected) => string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase);

[GeneratedRegex(@"^\[(?<name>(?:[^#;]|\\#|\\;)+)\]\s*(?:[#;].*)?$", RegexOptions.None, 1000)]
[GeneratedRegex(@"^\[(?<name>(?:[^#;]|\\#|\\;)+)\]\s*(?:[#;].*)?$", RegexOptions.None, Timeout.Infinite)]
private static partial Regex SectionLine();

// Quadratic, so it keeps a finite match timeout: the lazy value and the trailing whitespace run overlap, so a long run of spaces rescans.
[GeneratedRegex(@"^(?<key>[\w.\-]+)\s*[=:]\s*(?<value>.*?)\s*(?:[#;].*)?$", RegexOptions.None, 1000)]
private static partial Regex PropertyLine();

[GeneratedRegex(@"^(?:[A-Za-z]{1,8}[0-9]{3,5}|dotnet_code_quality)[._]", RegexOptions.IgnoreCase, 1000)]
[GeneratedRegex(@"^(?:[A-Za-z]{1,8}[0-9]{3,5}|dotnet_code_quality)[._]", RegexOptions.IgnoreCase, Timeout.Infinite)]
private static partial Regex RuleOption();
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ private static bool IsComment(SyntaxTrivia trivia) =>
|| trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)
|| trivia.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia);

[GeneratedRegex(@"^slopwatch-ignore(?:-start)?\s*:\s*(?<rule>SW\d{3})\b", RegexOptions.IgnoreCase, 1000)]
[GeneratedRegex(@"^slopwatch-ignore(?:-start)?\s*:\s*(?<rule>SW\d{3})\b", RegexOptions.IgnoreCase, Timeout.Infinite)]
private static partial Regex SlopwatchIgnore();

[GeneratedRegex(@"^#:property\s+(?<name>[A-Za-z]+)\s*=(?<value>.*)$", RegexOptions.None, 1000)]
[GeneratedRegex(@"^#:property\s+(?<name>[A-Za-z]+)\s*=(?<value>.*)$", RegexOptions.None, Timeout.Infinite)]
private static partial Regex PropertyDirective();
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,6 @@ private static string Condition(XElement element)

private static bool Is(string actual, string expected) => string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase);

[GeneratedRegex(@"(?:^|\s)(?:-p:|/p:|--property:|-property:)(?<name>[A-Za-z]+)=(?<value>""[^""]*""|'[^']*'|\S+)", RegexOptions.Multiline, 1000)]
[GeneratedRegex(@"(?:^|\s)(?:-p:|/p:|--property:|-property:)(?<name>[A-Za-z]+)=(?<value>""[^""]*""|'[^']*'|\S+)", RegexOptions.Multiline, Timeout.Infinite)]
private static partial Regex CommandLineProperty();
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ public static string Render(string text, SnapshotReceipt receipt, DateOnly date)
+ @"\| Upstream release tag \| `[^`\r\n]+` \|\n\| Commit \| \S+ \|\n"
+ @"\| Upstream product channel \| npm `@opencode/cli@latest` \(channel `latest`\); this tag published as `[^`\r\n]+`, "
+ @"installing the `opencode` command \(plus the transitional `opencode2` alias\) \|$",
RegexOptions.Multiline, matchTimeoutMilliseconds: 1000)]
RegexOptions.Multiline, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex IdentityTableRegex();

[GeneratedRegex(@"This pin is the release tag `[^`\r\n]+`, so\n`npm install -g @opencode/cli@[^`\r\n]+` installs a server built from exactly this commit; later",
RegexOptions.None, matchTimeoutMilliseconds: 1000)]
RegexOptions.None, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex PinProseRegex();

[GeneratedRegex(@"^Date: \d{4}-\d{2}-\d{2}$", RegexOptions.Multiline, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex(@"^Date: \d{4}-\d{2}-\d{2}$", RegexOptions.Multiline, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex DateLineRegex();
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public static SnapshotReceipt ParseReceipt(byte[] bytes, string path)
public static string SerializeReceipt(SnapshotReceipt receipt) =>
JsonSerializer.Serialize(receipt, ToolJsonContext.Default.SnapshotReceipt) + "\n";

[GeneratedRegex("^[0-9a-f]{40}$", RegexOptions.None, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex("^[0-9a-f]{40}$", RegexOptions.None, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex FullShaRegex();

private async Task<byte[]?> ReadOptionalAsync(string path, CancellationToken cancellationToken) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,6 @@ private static void ValidateSource(WatchedSource source)
}
}

[GeneratedRegex("^[0-9a-f]{64}$", RegexOptions.None, matchTimeoutMilliseconds: 1000)]
[GeneratedRegex("^[0-9a-f]{64}$", RegexOptions.None, matchTimeoutMilliseconds: Timeout.Infinite)]
private static partial Regex Sha256Regex();
}
Loading