Skip to content

Feature: Project initialization and target architecture#21

Open
lrottach wants to merge 6 commits into
mainfrom
feature/project-initialization
Open

Feature: Project initialization and target architecture#21
lrottach wants to merge 6 commits into
mainfrom
feature/project-initialization

Conversation

@lrottach

@lrottach lrottach commented Apr 17, 2026

Copy link
Copy Markdown
Owner

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:

  • C# sources under src/AzureChronos.Functions/ and src/AzureChronos.Tests/, the solution, docker-compose.yml, IDE config
  • Bicep templates under deploy/
  • CI workflows under .github/workflows/ (.NET Tests, CodeQL, Qodana)
  • .github/qodana.yaml

src/, deploy/, and .github/workflows/ are kept as empty placeholders via .gitkeep so 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.pr set to "" to suppress Claude co-author / "Generated with Claude Code" trailers
  • Permission allowlist for common git commands and read-only gh wrappers (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.json so personal Claude overrides stay out of the repo.

Target architecture (new)

Added a new ## Architecture chapter to README.md documenting the operational model for the rewrite:

  • Hosting: Flex Consumption plan, .NET 10 isolated worker (Linux).
  • Orchestration: Durable Functions on the Azure Storage backend.
  • Components: DiscoveryTimer (5-min ARG query) → VmScheduleEntity (Durable Entity per VM) → eternal VmScheduler orchestrator → idempotent start/stop activities calling Azure.ResourceManager.Compute via user-assigned managed identity.
  • Tag mutation flow: tag edit, full removal, and partial removal all reduce to one mechanism — discovery signals the entity → entity raises ScheduleChanged → orchestrator reacts and ContinueAsNews (or exits cleanly on full removal).
  • Discovery: single Azure Resource Graph KQL query, multi-subscription-ready (single-sub for dev).
  • RBAC: minimal custom role (Reader + Compute/start/action + Compute/deallocate/action).
  • Out of scope for v1: Event Grid reconciliation, manual override API, non-VM resource types, Durable Task Scheduler backend.
  • Why Durable Functions, not the standalone Durable Task SDK: explicit evaluation recorded so the question doesn't resurface without context.

.claude/CLAUDE.md updated 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)

  • Scope: single subscription for dev; multi-subscription is a v-next requirement (already accommodated by ARG-based discovery).
  • Scale: < ~100 VMs expected → Azure Storage backend is comfortable; DTS unnecessary.
  • Reconciliation latency: 5-minute discovery acceptable; Event Grid deferred.

Review notes

  • Branch protection on main is expected to be configured in GitHub settings (outside this PR).
  • The new src/ and infra/ contents, azure.yaml for azd, new CI workflows, a fully rewritten README.md, and concrete tag names are all deferred to follow-up features.

Test plan

  • git log --oneline on main after merge shows the four commits following the convention (feat: × 2, docs: × 2)
  • ls src/ deploy/ .github/workflows/ show only .gitkeep on the merged branch
  • README.md ## Architecture chapter renders correctly on github.com (diagram in code block, links resolve)
  • Future Claude Code sessions pick up .claude/CLAUDE.md and respect the settings (attribution suppressed, permissions honored, architecture treated as fixed)

Lukas Rottach added 2 commits April 17, 2026 20:56
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.
@lrottach lrottach self-assigned this Apr 17, 2026
@lrottach lrottach added this to the v1.0.0-pre-release milestone Apr 17, 2026
@lrottach lrottach added the feature New feature or request label Apr 17, 2026
Lukas Rottach added 2 commits April 18, 2026 09:43
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.
@lrottach lrottach changed the title Feature: Project initialization Feature: Project initialization and target architecture Apr 18, 2026
Lukas Rottach added 2 commits April 18, 2026 10:03
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.

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.

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.md and 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>

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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).

Suggested change
<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>

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +18

[Function("AzureChronos")]
public void Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer)
{

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +102 to +109
### 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).

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment thread src/global.json
Comment on lines +1 to +7
{
"sdk": {
"version": "10.0.105",
"rollForward": "latestFeature",
"allowPrerelease": false
}
}

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment thread .vscode/settings.json
@@ -1,9 +1,9 @@
{
"azureFunctions.deploySubpath": "src/AzureChronos.Functions/bin/Release/net6.0/publish",
"azureFunctions.deploySubpath": "src/bin/Release/net10.0/publish",

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
"azureFunctions.deploySubpath": "src/bin/Release/net10.0/publish",
"azureFunctions.deploySubpath": "src/AzureChronos.Functions/bin/Release/net10.0/publish",

Copilot uses AI. Check for mistakes.
Comment thread .vscode/settings.json
"debug.internalConsoleOptions": "neverOpen",
"azureFunctions.projectSubpath": "src/AzureChronos.Functions",
"azureFunctions.preDeployTask": "publish (functions)",
"dotnet.defaultSolution": "src/AzureChronos.sln"

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
"dotnet.defaultSolution": "src/AzureChronos.sln"
"dotnet.defaultSolution": "src/AzureChronos.slnx"

Copilot uses AI. Check for mistakes.
Comment thread .vscode/tasks.json
"type": "func",
"dependsOn": "build (functions)",
"options": {
"cwd": "${workspaceFolder}/src/bin/Debug/net10.0"

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
"cwd": "${workspaceFolder}/src/bin/Debug/net10.0"
"cwd": "${workspaceFolder}/src/AzureChronos.Functions"

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants