Feature: Project initialization and target architecture#21
Conversation
Remove the abandoned .NET 6 Azure Chronos project (C# sources, Bicep templates, CI workflows, Qodana config) so subsequent features can build the new Azure Functions / Durable Functions engine on .NET 10 from a clean slate. Keep src/, deploy/, and .github/workflows/ as empty placeholders via .gitkeep. Add .claude/CLAUDE.md capturing project intent, target stack (Azure Functions isolated worker, Durable Functions, azd + Terraform), tag-driven scheduling scenarios, git and PR conventions, and the commit-approval rule.
Add .claude/settings.json disabling Claude commit and PR attribution and allowlisting common git commands plus read-only gh wrappers (issue/pr/repo list, view, status, diff, checks). Gitignore .claude/settings.local.json so personal overrides stay out of the repo.
Append a new Architecture chapter documenting the target operational model for the Chronos rewrite: Flex Consumption + .NET 10 isolated + Durable Functions on Azure Storage, with a Durable Entity per VM, an eternal per-VM orchestrator, and start/stop activities. Includes the component diagram, tag-mutation flow (edit / removal / partial), the ARG-based discovery model, the minimal-RBAC managed identity, the explicit v1 out-of-scope list, and a record of why the standalone Durable Task SDK was evaluated and rejected. Marked as the target architecture; the legacy content above will be replaced once the new implementation lands.
Replace the "(choice deferred)" hedge in Target stack with the confirmed Durable Entity + eternal orchestrator decision, plus Flex Consumption hosting, Azure Storage backend, and ARG discovery. Add an Architecture (decided) section pointing at the README for the full diagram and rationale. Tighten the working-style notes: architecture is now fixed and shouldn't be relitigated; tag names, manual-override API, and Event Grid integration remain open and require asking the user.
Add a Timer-triggered Azure Functions project from the official template under src/AzureChronos.Functions/. Targets net10.0, isolated worker (Microsoft.Azure.Functions.Worker 2.51.0), ASP.NET Core integration, Application Insights pre-wired. The sample timer fires every 5 minutes — placeholder for the future DiscoveryTimer. Drop src/.gitkeep now that the folder has real content. Update root .vscode launch/tasks/settings to drive the new project (auto-generated by the Functions extension).
- Rename src.csproj to AzureChronos.Functions.csproj so the assembly output and project identity match the folder name. - Add src/AzureChronos.slnx referencing the Functions project (.NET 10 default .slnx format). - Add src/global.json pinning the SDK to 10.0.105 with rollForward=latestFeature. Build verified: dotnet build src/AzureChronos.slnx succeeds with no warnings.
There was a problem hiding this comment.
Pull request overview
Initializes the repository for the “Chronos for Azure” rewrite by removing the legacy .NET 6/Bicep/CI setup, adding repo-level tooling/conventions, and documenting the target architecture for upcoming feature work.
Changes:
- Removed legacy .NET 6 Azure Functions/Durable code, tests, Bicep templates, and CI workflows.
- Added a .NET 10 isolated-worker Functions project scaffold (Program + minimal TimerTrigger) plus SDK pinning and updated VS Code settings/tasks.
- Added architecture documentation to
README.mdand Claude Code repo guidance/config under.claude/.
Reviewed changes
Copilot reviewed 43 out of 48 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/global.json | Pins .NET SDK for work under src/. |
| src/docker-compose.yml | Removes legacy Azurite compose file. |
| src/AzureChronos.slnx | Adds new solution format referencing Functions project. |
| src/AzureChronos.sln | Removes legacy .sln. |
| src/AzureChronos.Tests/Usings.cs | Removes legacy test global using. |
| src/AzureChronos.Tests/TimeCalculationServicesTests.cs | Removes legacy unit tests. |
| src/AzureChronos.Tests/AzureChronos.Tests.csproj | Removes legacy test project. |
| src/AzureChronos.Functions/readme.md | Adds Functions template readme placeholder. |
| src/AzureChronos.Functions/host.json | Updates host configuration (App Insights live metrics filters). |
| src/AzureChronos.Functions/Startup.cs | Removes in-process FunctionsStartup DI wiring. |
| src/AzureChronos.Functions/Services/AzureComputeService.cs | Removes legacy ARM compute service. |
| src/AzureChronos.Functions/Properties/launchSettings.json | Updates local launch profile/port. |
| src/AzureChronos.Functions/Program.cs | Adds isolated worker entry point and App Insights wiring. |
| src/AzureChronos.Functions/Models/EntityInitializePayload.cs | Removes legacy model. |
| src/AzureChronos.Functions/Interfaces/IVirtualMachineEntity.cs | Removes legacy entity interface. |
| src/AzureChronos.Functions/Interfaces/IAzureComputeService.cs | Removes legacy compute interface. |
| src/AzureChronos.Functions/Entities/VirtualMachineEntity.cs | Removes legacy Durable Entity implementation. |
| src/AzureChronos.Functions/Common/TagHandler.cs | Removes legacy tag helper. |
| src/AzureChronos.Functions/Common/StringHandler.cs | Removes legacy resource ID parsing helper. |
| src/AzureChronos.Functions/Common/CronHandler.cs | Removes legacy cron helper. |
| src/AzureChronos.Functions/AzureChronosTimerTrigger.cs | Removes legacy in-process timer trigger. |
| src/AzureChronos.Functions/AzureChronos.cs | Adds minimal TimerTrigger function (isolated worker). |
| src/AzureChronos.Functions/AzureChronos.Functions.csproj | Retargets to net10.0 isolated worker packages. |
| src/AzureChronos.Functions/.gitignore | Retains project-level gitignore (line-ending/formatting changes). |
| src/.vscode/tasks.json | Removes legacy per-src/ VS Code tasks. |
| src/.vscode/settings.json | Removes legacy per-src/ VS Code settings. |
| src/.vscode/launch.json | Removes legacy per-src/ launch config. |
| src/.vscode/extensions.json | Removes legacy per-src/ extension recommendations. |
| src/.run/Azurite.run.xml | Removes legacy Rider run config for Azurite. |
| src/.run/Azurite Compose.run.xml | Removes legacy Rider run config for compose. |
| src/.gitignore | Removes legacy src/-scoped gitignore. |
| deploy/param.prod.bicepparam | Removes legacy prod Bicep params. |
| deploy/param.dev.bicepparam | Removes legacy dev Bicep params. |
| deploy/modules/Microsoft.Web/functionapp.module.bicep | Removes legacy Function App Bicep module. |
| deploy/main.bicep | Removes legacy deployment entry Bicep. |
| deploy/.gitkeep | Keeps deploy/ directory tracked as placeholder. |
| README.md | Adds “target architecture” chapter describing the rewrite design. |
| .vscode/tasks.json | Updates repo-level VS Code tasks for new layout/.NET 10. |
| .vscode/settings.json | Updates repo-level VS Code Azure Functions + solution settings. |
| .vscode/launch.json | Re-formats/updates repo-level launch configuration. |
| .gitignore | Adds .claude/settings.local.json to ignored files. |
| .github/workflows/qodana_code_quality.yml | Removes legacy Qodana workflow. |
| .github/workflows/dotnet-desktop.yml | Removes legacy .NET 6 test workflow. |
| .github/workflows/codeql.yml | Removes legacy CodeQL workflow. |
| .github/workflows/.gitkeep | Keeps workflows directory tracked as placeholder. |
| .github/qodana.yaml | Removes legacy Qodana config. |
| .claude/settings.json | Adds Claude Code project settings (attribution + command allowlist). |
| .claude/CLAUDE.md | Adds Claude Code repo guidance aligned to the target architecture. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.3.1" /> | ||
| <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" /> | ||
| </ItemGroup> | ||
|
|
There was a problem hiding this comment.
The project file no longer marks host.json (and local.settings.json, if present) for copy/publish. For Azure Functions, host.json must be included in the published output; otherwise the deployed app (and local func host start from output folders) may fail to start. Re-add the None Update="host.json" / CopyToOutputDirectory and CopyToPublishDirectory metadata (and similar for local.settings.json).
| <ItemGroup> | |
| <None Update="host.json"> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | |
| </None> | |
| <None Update="local.settings.json"> | |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |
| <CopyToPublishDirectory>Never</CopyToPublishDirectory> | |
| </None> | |
| </ItemGroup> |
|
|
||
| [Function("AzureChronos")] | ||
| public void Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer) | ||
| { |
There was a problem hiding this comment.
PR description states it "does not introduce any new Functions"; however this change adds a TimerTrigger function ([Function("AzureChronos")]). Either remove the placeholder function from this PR or update the PR description to reflect that a new Functions entry point is being introduced.
| ### Out of scope for v1 | ||
|
|
||
| - Event Grid subscription on tag changes (sub-second reconciliation) — additive enhancement, slots in as a second signal source on the same entity. | ||
| - Manual override HTTP API (e.g. "skip next start"). | ||
| - Resource types other than VMs. | ||
| - Durable Task Scheduler backend — unnecessary at this scale. | ||
| - Concrete Chronos tag names (still to be decided). | ||
|
|
There was a problem hiding this comment.
The new architecture section says concrete Chronos tag names are "still to be decided", but the legacy section above this still lists specific tag names (e.g., AzChronos_Startup, AzChronos_Deallocate, etc.). To avoid conflicting guidance, consider explicitly marking the legacy tag table as legacy/outdated or removing the "tag names still to be decided" bullet until the legacy section is replaced.
| { | ||
| "sdk": { | ||
| "version": "10.0.105", | ||
| "rollForward": "latestFeature", | ||
| "allowPrerelease": false | ||
| } | ||
| } |
There was a problem hiding this comment.
global.json is placed under src/, so it will only be honored when running dotnet from within src (or subfolders). Running dotnet build/test from the repo root (common in CI and some IDEs) will ignore this SDK pin. Consider moving global.json to the repo root (or adding a root one) so the SDK version is applied consistently.
| @@ -1,9 +1,9 @@ | |||
| { | |||
| "azureFunctions.deploySubpath": "src/AzureChronos.Functions/bin/Release/net6.0/publish", | |||
| "azureFunctions.deploySubpath": "src/bin/Release/net10.0/publish", | |||
There was a problem hiding this comment.
azureFunctions.deploySubpath points to src/bin/Release/net10.0/publish, but the Functions project publishes under src/AzureChronos.Functions/bin/Release/net10.0/publish. As written, VS Code deployments will likely pick up a non-existent folder.
| "azureFunctions.deploySubpath": "src/bin/Release/net10.0/publish", | |
| "azureFunctions.deploySubpath": "src/AzureChronos.Functions/bin/Release/net10.0/publish", |
| "debug.internalConsoleOptions": "neverOpen", | ||
| "azureFunctions.projectSubpath": "src/AzureChronos.Functions", | ||
| "azureFunctions.preDeployTask": "publish (functions)", | ||
| "dotnet.defaultSolution": "src/AzureChronos.sln" |
There was a problem hiding this comment.
dotnet.defaultSolution references src/AzureChronos.sln, but this PR removes the .sln and adds src/AzureChronos.slnx. Update the setting (or re-add a .sln) so C# tooling that relies on the default solution doesn't break.
| "dotnet.defaultSolution": "src/AzureChronos.sln" | |
| "dotnet.defaultSolution": "src/AzureChronos.slnx" |
| "type": "func", | ||
| "dependsOn": "build (functions)", | ||
| "options": { | ||
| "cwd": "${workspaceFolder}/src/bin/Debug/net10.0" |
There was a problem hiding this comment.
The Functions host task runs with cwd set to ${workspaceFolder}/src/bin/Debug/net10.0, but the build output for the project is under src/AzureChronos.Functions/bin/Debug/net10.0 (and func host start typically expects to run from the Function app root where host.json lives). Update the working directory so host start can find the right artifacts/config.
| "cwd": "${workspaceFolder}/src/bin/Debug/net10.0" | |
| "cwd": "${workspaceFolder}/src/AzureChronos.Functions" |
Summary
Initialize the rewrite of this repository as Chronos for Azure — a lightweight Azure Functions / Durable Functions engine that starts and stops Azure VMs based on CRON schedules supplied via Azure Tags — and document the target architecture so subsequent feature PRs can build against an agreed design.
This PR does not introduce any new Functions or infrastructure code. It clears the abandoned .NET 6 project, establishes conventions and tooling, and records the architectural decisions for the rewrite.
Changes
Project initialization
Legacy cleanup — removed the old .NET 6 Azure Chronos project:
src/AzureChronos.Functions/andsrc/AzureChronos.Tests/, the solution,docker-compose.yml, IDE configdeploy/.github/workflows/(.NET Tests, CodeQL, Qodana).github/qodana.yamlsrc/,deploy/, and.github/workflows/are kept as empty placeholders via.gitkeepso tracking survives until the rewrite populates them..claude/CLAUDE.md— project guide for future Claude Code sessions: project intent, target stack, scheduling scenarios, git/PR conventions, and the commit-approval rule..claude/settings.json— project-scoped Claude Code config:attribution.commit/attribution.prset to""to suppress Claude co-author / "Generated with Claude Code" trailersghwrappers (issue list/view/status,pr list/view/status/diff/checks,repo view)gh api *intentionally not allowed — it can perform writes via-X POST/PATCH/DELETE.gitignore— added.claude/settings.local.jsonso personal Claude overrides stay out of the repo.Target architecture (new)
Added a new
## Architecturechapter toREADME.mddocumenting the operational model for the rewrite:DiscoveryTimer(5-min ARG query) →VmScheduleEntity(Durable Entity per VM) → eternalVmSchedulerorchestrator → idempotent start/stop activities callingAzure.ResourceManager.Computevia user-assigned managed identity.ScheduleChanged→ orchestrator reacts andContinueAsNews (or exits cleanly on full removal).Reader+Compute/start/action+Compute/deallocate/action)..claude/CLAUDE.mdupdated to align with these decisions: Target stack now states the confirmed Durable Entities + eternal orchestrator choice; new Architecture (decided) section points at the README; working-style notes tightened so the architecture isn't relitigated, while genuinely open questions (tag names, override API, Event Grid timing) still require asking the user.Decisions confirmed by the user (recorded for future reference)
Review notes
mainis expected to be configured in GitHub settings (outside this PR).src/andinfra/contents,azure.yamlforazd, new CI workflows, a fully rewrittenREADME.md, and concrete tag names are all deferred to follow-up features.Test plan
git log --onelineonmainafter merge shows the four commits following the convention (feat:× 2,docs:× 2)ls src/ deploy/ .github/workflows/show only.gitkeepon the merged branchREADME.md## Architecturechapter renders correctly on github.com (diagram in code block, links resolve).claude/CLAUDE.mdand respect the settings (attribution suppressed, permissions honored, architecture treated as fixed)