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
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -709,3 +709,15 @@ dotnet_diagnostic.MA0042.severity = none
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.MA0004.severity = none
dotnet_diagnostic.VSTHRD111.severity = none

# =============================================================================
# FILE-BASED ENTRY ARBITRATION (MA0048)
# =============================================================================
# Placed after every global C# diagnostic section so the file-scoped glob never
# narrows a later section. MA0048 (file name = type name) cannot apply to the
# file-based tool entry: the SDK synthesizes the Program type, and the sealed
# entry contract pins the file name (it is the tool name:
# ./tools/opencode-tool.cs). Winner: the entry contract; the rule stays error
# for every other file, generated output included (emission keeps file = type).
[tools/opencode-tool.cs]
dotnet_diagnostic.MA0048.severity = none
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ jobs:
if: runner.os == 'Linux'
run: dotnet format --verify-no-changes --no-restore

# Smoke-runs the repo tooling entry; exercises the committed executable bit and the
# shebang, which only take effect on a Linux checkout.
- name: Tooling entry smoke
if: runner.os == 'Linux'
run: ./tools/opencode-tool.cs --help

# TUnit's always-on HTML report self-uploads as a workflow artifact, but only if these
# runtime vars (normally JS/Docker-action-only) are re-exported to run steps.
- name: Enable TUnit report artifact upload
Expand Down
4 changes: 4 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageVersion Include="Aspire.Hosting" Version="13.4.6"/>
<PackageVersion Include="Aspire.Hosting.AppHost" Version="13.4.6"/>
<!-- microsoft packages -->
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.10"/>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.301"/>
<!-- downlevel targets (netstandard2.0 / net472) -->
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3"/>
Expand All @@ -24,10 +25,13 @@
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.17.0"/>
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.17.0"/>
<!-- third-party packages -->
<PackageVersion Include="Spectre.Console.Cli" Version="0.55.0"/>
<PackageVersion Include="Spectre.Console.Cli.Extensions.DependencyInjection" Version="0.27.0"/>

<!-- test packages -->
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.4.6"/>
<PackageVersion Include="NSubstitute" Version="6.0.0"/>
<PackageVersion Include="Spectre.Console.Cli.Testing" Version="0.55.0"/>
<PackageVersion Include="TUnit" Version="1.63.25"/>
</ItemGroup>
</Project>
5 changes: 5 additions & 0 deletions OpenCode.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@
</Folder>
<Folder Name="/tests/">
<Project Path="tests/OpenCode.Sdk.Tests/OpenCode.Sdk.Tests.csproj" />
<Project Path="tests/OpenCode.Sdk.Tools.Tests/OpenCode.Sdk.Tools.Tests.csproj" />
</Folder>
<Folder Name="/tools/">
<Project Path="tools/OpenCode.Sdk.Tools/OpenCode.Sdk.Tools.csproj" />
<File Path="tools/opencode-tool.cs" />
</Folder>
</Solution>
13 changes: 8 additions & 5 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ as of 2026-08-08. Design runway complete and grill-hardened: three sealed specs
`superpowers/specs/` (public API, generator architecture, testing architecture), ADRs 0001–0009,
research log sessions 1–9. Implementation planning complete (2026-08-10): slice map at
`superpowers/plans/2026-08-10-implementation-slice-map.md` (12 vertical slices), slice issues
**#1–#12** with native `blocked_by` ordering, slice 0 detail plan written (issue #1 is
`ready-for-agent`); deviation protocol added (`docs/agents/deviation-protocol.md`), falsifiability
working agreement added to `AGENTS.md`. No SDK code yet — next up: execute slice 0 (issue #1).
**#1–#12** with native `blocked_by` ordering; deviation protocol added
(`docs/agents/deviation-protocol.md`), falsifiability working agreement added to `AGENTS.md`.
The Slice 0 tooling skeleton has landed; next is the Slice 1 planning cycle for issue #2.

## Queue

In order — do not improvise beyond it without asking the maintainer.

1. **SDK build-out — slice issues #1–#12.** Scope, sequencing, and the per-slice done
1. **Post-Slice-0 Slopwatch adoption evaluation.** Decide local-tool pinning, baseline/config,
and CI policy together as one deliberate adoption decision. Do not add a pin, baseline,
configuration, or CI gate until that decision is made.
2. **SDK build-out — remaining slice issues #2–#12.** Scope, sequencing, and the per-slice done
definition live in the slice map
(`superpowers/plans/2026-08-10-implementation-slice-map.md`); task-level progress lives in
per-slice plan checkboxes (plans written just-in-time; a slice whose plan exists gets
Expand All @@ -34,7 +37,7 @@ In order — do not improvise beyond it without asking the maintainer.
lock the public surface as implementation lands (testing spec §12; slices 4–5). **Spec
retirement rides slice 11:** durable sealed decisions without an ADR home distill into ADRs
before the transient `docs/superpowers/` documents are deleted.
2. **Later** — **MCP server**: in this repo as a thin SDK adapter (ADR-0006). Tech targeting
3. **Later** — **MCP server**: in this repo as a thin SDK adapter (ADR-0006). Tech targeting
decided 2026-08-08 (research doc 05), **re-sealed at phase start against the then-current
MCP landscape:** the 2026-07-28 protocol revision via MCP C# SDK v2.0, stdio + streamable
HTTP, no investment in deprecated features. Evaluate NuGet's `McpServer` package type for
Expand Down
75 changes: 75 additions & 0 deletions docs/research/00-research-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -970,3 +970,78 @@ TUnit reads arbitrary nested keys via `TestContext.Configuration.Get` — the
`testingPlatform.environmentVariables` section did **not** apply and is not relied upon.

**Decision:** the TUnit block leaves §14; results recorded at their §5.1/§5.4 use sites.

# Session 10 — 2026-08-10: slice 0 build-out — file-based entry verification

Closing session for slice 0's tooling skeleton: ran the generator spec §3.3 three-item
verification list against the committed file-based entry
(`tools/opencode-tool.cs` → `OpenCode.Sdk.Tools` via the `#:project` directive) and
recorded the Task 3 MA0048 arbitration trace in its research home.

## Q52: Does the file-based entry survive the §3.3 verification list?

**How researched:** the three §3.3 checks run live against the entry from the repo root.
(1) Strict-props build: `dotnet run --file tools/opencode-tool.cs -- generate` — the
entry inherits `Directory.Build.props` strict props on the cached `dotnet run` path, so a
green build under the full analyzer wall + TWAE is the proof. (2) Cache staleness: with
the entry warm, the `GenerateCommand` stub message was mutated to append
` (cache-probe)` via a file-only edit (no `dotnet build` between edit and re-run), then
the entry was re-run. (3) Invocation forms: `dotnet run --file … -- generate` (any OS)
and `--help` were exercised; the Unix direct-invocation form `./tools/opencode-tool.cs`
was pinned (mode 100755, shebang) but not run on this Windows host — Linux CI runs it in
Task 5. Task 3's strict-props build at session 9 had hit MA0048 on the synthesized
`Program` behind the sealed entry filename; that arbitration is included below as the
required strict-props finding trace.

**Found (per item):**

- *Strict-props build verdict:* clean. Post-arbitration, the entry builds under the
inherited strict props with `0 Warning(s) 0 Error(s)` and the fail-loud stub prints
`generate is not implemented yet — the generator pipeline has not landed.` (exit 1,
captured not misclassified). MA0048 (mezintou/Meziantou.Analyzer — "File name must
match type name") fired on the first run because the SDK synthesizes a `Program` type
behind a sealed entry filename no MA0048 mode (`Exact`/`Prefix`/`LongestCommonPrefix`)
can match. Classified as a **Level 1 recorded fallback** (deviation protocol Level 1;
AGENTS.md Hard Rule — "when a rule misfires on real code, the move is a per-rule
arbitration comment naming the winner — never a policy rollback"). The pre-authorized
remedy is a file-scoped arbitration (`.editorconfig`'s last section:
`[tools/opencode-tool.cs]` → `dotnet_diagnostic.MA0048.severity = none`, comment
naming the sealed entry contract as winner); it silences MA0048 for that file only,
and MA0048 stays `error` for every other file (emission file = type commitment
intact). This is a recorded fallback, not a policy rollback — the rule remains on for
the rest of the codebase, generated output included. The §3.3 cache-staleness
fallbacks (routed-build Level 1; console-app promotion + ADR-0003 correction Level 2)
are independent of MA0048 and were not triggered by it: their subject is cache
staleness, evaluated under the staleness check below. Initial placement of the
file-scoped section silently narrowed the global `[*.cs]` scope (`.editorconfig`
section headers are cumulative until the next header); a fix-up commit moved the
arbitration to the file's last section so it no longer narrows any subsequent section.

- *Staleness verdict:* **no mitigation needed.** After the ` (cache-probe)` edit and
re-running `dotnet run --file tools/opencode-tool.cs -- generate` without an
intervening `dotnet build`, the output read
`generate is not implemented yet — the generator pipeline has not landed.
(cache-probe)` (exit 1) — the freshly edited `GenerateCommand` ran. The marker
appeared, so `#:project` library changes trigger rebuilds on the file-based entry path;
the stale-tool hazard does not exist for this entry shape. The generator spec §3.3
routed-build Level 1 fallback was therefore **not triggered**, and the two-condition
fallback (console-app promotion + ADR-0003 correction) stayed dormant. Probe edit
reverted by apply_patch; the `GenerateCommand.cs` blob was bit-identical pre- and
post-task (SHA256 `de69cb4e…`, git blob `df921b4…`).

- *Invocation forms:* `dotnet run --file tools/opencode-tool.cs -- generate` exits 1 with
the stub message; `dotnet run --file tools/opencode-tool.cs -- --help` exits 0 with
usage listing `generate — Regenerate the SDK model layer from spec/openapi.json`. The
Unix direct-invocation form `./tools/opencode-tool.cs <args>` is pinned by mode 100755
+ the `#!/usr/bin/env -S dotnet --` shebang and awaits Linux CI verification in Task 5.

**Decision:** §3.3 verification list passes; the file-based entry survives as sealed.
Cache mitigation is **off** — Task 5's CI smoke step does **not** prepend a routed
`dotnet build` line; the §3.3 cache-staleness fallbacks were **not triggered**
(marker-present path: routed-build Level 1 unneeded; console-app promotion + ADR-0003
correction Level 2 dormant). MA0048 is the **one Level 1 recorded fallback executed in
this slice** — pre-authorized by the analyzer-policy Hard Rule's per-rule arbitration
procedure (a recorded fallback, not a policy rollback); the file-scoped arbitration is
the status quo for this one entry file. If the entry contract ever moves (e.g.
console-app promotion at a future cache-Level-2 trigger), the arbitration moves with it
or is deleted.
52 changes: 26 additions & 26 deletions docs/superpowers/plans/2026-08-10-slice-00-tooling-skeleton.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Microsoft.Extensions.DependencyInjection, TUnit (already pinned).
- Produces: an empty net10.0 library project that later tasks fill; the `/tools/` solution
folder.

- [ ] **Step 1: Add package pins to `Directory.Packages.props`**
- [x] **Step 1: Add package pins to `Directory.Packages.props`**

Under `<!-- microsoft packages -->` add:

Expand All @@ -78,7 +78,7 @@ Under `<!-- test packages -->` add:
<PackageVersion Include="Spectre.Console.Cli.Testing" Version="0.55.0"/>
```

- [ ] **Step 2: Create `tools/OpenCode.Sdk.Tools/OpenCode.Sdk.Tools.csproj`**
- [x] **Step 2: Create `tools/OpenCode.Sdk.Tools/OpenCode.Sdk.Tools.csproj`**

```xml
<Project Sdk="Microsoft.NET.Sdk">
Expand All @@ -100,7 +100,7 @@ Under `<!-- test packages -->` add:
(`$(DefaultTargetFramework)` is `net10.0` from `Directory.Build.props`; the tool is
single-TFM by design — testing spec §4.)

- [ ] **Step 3: Add the project to `OpenCode.slnx`**
- [x] **Step 3: Add the project to `OpenCode.slnx`**

Insert after the `/tests/` folder (folders are kept alphabetical):

Expand All @@ -110,14 +110,14 @@ Insert after the `/tests/` folder (folders are kept alphabetical):
</Folder>
```

- [ ] **Step 4: Build**
- [x] **Step 4: Build**

Run: `dotnet build --configuration Release`
Expected: success, zero warnings (the empty library must pass the wall — if a repo-wide
analyzer fires on the bare project, that is a finding, not something to suppress; classify
per the deviation protocol).

- [ ] **Step 5: Commit**
- [x] **Step 5: Commit**

```bash
git add Directory.Packages.props tools/OpenCode.Sdk.Tools/OpenCode.Sdk.Tools.csproj OpenCode.slnx
Expand Down Expand Up @@ -149,7 +149,7 @@ API note: `CommandAppTester` (ctor taking `ITypeRegistrar`, `Configure`, `RunAsy
pinned version's signatures differ in detail (e.g. an added `CancellationToken` on
`AsyncCommand.ExecuteAsync`), adapt in place — that is a level-0 deviation.

- [ ] **Step 1: Create the test project**
- [x] **Step 1: Create the test project**

`tests/OpenCode.Sdk.Tools.Tests/OpenCode.Sdk.Tools.Tests.csproj`:

Expand Down Expand Up @@ -179,7 +179,7 @@ Add to `OpenCode.slnx` under the existing `/tests/` folder:
<Project Path="tests/OpenCode.Sdk.Tools.Tests/OpenCode.Sdk.Tools.Tests.csproj" />
```

- [ ] **Step 2: Write the failing tests**
- [x] **Step 2: Write the failing tests**

`tests/OpenCode.Sdk.Tools.Tests/ToolAppTests.cs`:

Expand Down Expand Up @@ -218,12 +218,12 @@ public sealed class ToolAppTests
(`OpenCode.Sdk.Tools.Tests` sits inside the `OpenCode.Sdk.Tools` namespace hierarchy, so
`ToolApp` resolves without a using directive — adding one would trip IDE0005.)

- [ ] **Step 3: Run tests to verify they fail**
- [x] **Step 3: Run tests to verify they fail**

Run: `dotnet test tests/OpenCode.Sdk.Tools.Tests`
Expected: build FAILS with CS0246 (`ToolApp` not found) — the red state for scaffolding.

- [ ] **Step 4: Implement `ToolApp`**
- [x] **Step 4: Implement `ToolApp`**

`tools/OpenCode.Sdk.Tools/ToolApp.cs`:

Expand Down Expand Up @@ -300,17 +300,17 @@ public sealed class GenerateCommand : AsyncCommand
}
```

- [ ] **Step 5: Run tests to verify they pass**
- [x] **Step 5: Run tests to verify they pass**

Run: `dotnet test tests/OpenCode.Sdk.Tools.Tests`
Expected: PASS (both tests, net10.0 leg).

- [ ] **Step 6: Full gate check**
- [x] **Step 6: Full gate check**

Run: `dotnet build --configuration Release` then `dotnet format --verify-no-changes --no-restore`
Expected: both clean.

- [ ] **Step 7: Commit**
- [x] **Step 7: Commit**

```bash
git add tools/OpenCode.Sdk.Tools tests/OpenCode.Sdk.Tools.Tests OpenCode.slnx
Expand All @@ -331,7 +331,7 @@ git commit -m "feat(tools): ToolApp composition root with fail-loud generate stu
pinned invocation forms (`dotnet run --file tools/opencode-tool.cs -- <args>` on any
OS; `./tools/opencode-tool.cs <args>` on Unix).

- [ ] **Step 1: Write the entry**
- [x] **Step 1: Write the entry**

`tools/opencode-tool.cs` — exactly this content, LF line endings, UTF-8 **without BOM**
(a BOM breaks the shebang):
Expand All @@ -349,7 +349,7 @@ Add to the `/tools/` folder in `OpenCode.slnx`:
<File Path="tools/opencode-tool.cs" />
```

- [ ] **Step 2: Run through the entry — §3.3 item 1 (strict-props build) + item 3 (invocation form)**
- [x] **Step 2: Run through the entry — §3.3 item 1 (strict-props build) + item 3 (invocation form)**

Run: `dotnet run --file tools/opencode-tool.cs -- generate`
Expected: exit code 1 and the stub message — proving the entry builds **clean under the
Expand All @@ -359,7 +359,7 @@ inherited strict props** (any analyzer/TWAE diagnostic fails this run) and that
Run: `dotnet run --file tools/opencode-tool.cs -- --help`
Expected: exit 0, help output listing `generate`.

- [ ] **Step 3: Commit the executable bit into the index**
- [x] **Step 3: Commit the executable bit into the index**

```bash
git add tools/opencode-tool.cs OpenCode.slnx
Expand All @@ -370,7 +370,7 @@ git ls-files -s tools/opencode-tool.cs
Expected: `git ls-files -s` prints mode `100755`. (The bit is meaningless on NTFS and
effective on the Linux CI checkout — generator spec §3.1.)

- [ ] **Step 4: Commit**
- [x] **Step 4: Commit**

```bash
git commit -m "feat(tools): file-based entry dogfooding ToolApp"
Expand All @@ -389,16 +389,16 @@ git commit -m "feat(tools): file-based entry dogfooding ToolApp"
- Produces: the recorded §3.3 verdict (research log, question→finding→decision) and, if
stale, the mitigation wired into CI/docs in Task 5.

- [ ] **Step 1: Baseline run**
- [x] **Step 1: Baseline run**

Run: `dotnet run --file tools/opencode-tool.cs -- generate` — note the message text.

- [ ] **Step 2: Mutate the referenced library without an explicit build**
- [x] **Step 2: Mutate the referenced library without an explicit build**

Edit the stub message in `GenerateCommand.cs` to append ` (cache-probe)`. Do **not** run
`dotnet build`. Re-run: `dotnet run --file tools/opencode-tool.cs -- generate`

- [ ] **Step 3: Read the verdict**
- [x] **Step 3: Read the verdict**

- Marker appears → `#:project` changes trigger rebuilds: **no mitigation needed**.
- Marker absent → the stale-tool hazard is real: **mitigation** = every documented/CI
Expand All @@ -409,12 +409,12 @@ Edit the stub message in `GenerateCommand.cs` to append ` (cache-probe)`. Do **n
promotion + one-line ADR-0003 correction) triggers — **stop; maintainer decision**
(level 2).

- [ ] **Step 4: Revert the probe edit**
- [x] **Step 4: Revert the probe edit**

Revert `GenerateCommand.cs` to Task 2's exact content; re-run the entry once to confirm
the original message.

- [ ] **Step 5: Record the findings**
- [x] **Step 5: Record the findings**

Append a session entry to `docs/research/00-research-log.md` (next session number,
`# Session N — <date>: slice 0 build-out — file-based entry verification`) with one
Expand All @@ -423,7 +423,7 @@ researched (the three checks above), found (per item: strict-props build verdict
staleness verdict, invocation forms), decision (mitigation on/off; fallback not
triggered / triggered).

- [ ] **Step 6: Commit**
- [x] **Step 6: Commit**

```bash
git add docs/research/00-research-log.md
Expand All @@ -441,7 +441,7 @@ git commit -m "docs(research): file-based entry verification results (slice 0)"
- Consumes: the entry + executable bit (Task 3), the staleness verdict (Task 4).
- Produces: the Linux-leg smoke step slice 3 will upgrade to `generate --verify`.

- [ ] **Step 1: Add the smoke step**
- [x] **Step 1: Add the smoke step**

After the `Verify formatting` step in `.github/workflows/ci.yml`:

Expand All @@ -461,21 +461,21 @@ If Task 4 recorded the staleness mitigation, prepend the routed build:
./tools/opencode-tool.cs --help
```

- [ ] **Step 2: Local full-gate sweep**
- [x] **Step 2: Local full-gate sweep**

Run, in order: `dotnet build --configuration Release`, `dotnet test --configuration Release --no-build`,
`dotnet format --verify-no-changes --no-restore`
Expected: all clean/green.

- [ ] **Step 3: Commit and push the branch**
- [x] **Step 3: Commit and push the branch**

```bash
git add .github/workflows/ci.yml
git commit -m "ci: smoke the tooling entry on the Linux leg"
git push -u origin feature/slice-00-tooling-skeleton
```

- [ ] **Step 4: Verify CI**
- [x] **Step 4: Verify CI**

Open the PR (`gh pr create`), watch the three legs; the Linux leg must run the smoke step
green. Slice exit: PR review + merge closes the slice issue.
Loading