You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix#460: skip redundant agent-identity S2S grant when role is inherited
When inheritable permissions are configured (allAllowed) and the blueprint SP
holds the app roles, the agent identity inherits them automatically — the same
basis the OBO/delegated path already relies on. The direct grant on the agent
identity SP was therefore redundant and, when the CLI token cannot write app
roles, produced a spurious "Action Required" PowerShell block on an otherwise
successful Global Admin run.
- Skip the per-identity grant (and the PowerShell prompt) when the blueprint
grant and inheritance both succeeded; report the inherited grant as Granted.
- When a direct grant is still needed (developer / non-inherited path), retry
via az rest before falling back to PowerShell, matching the blueprint grant.
- Correct stale README/CHANGELOG claim that Phase 2a/2b are always skipped for
non-DW agents (untrue since #421).
- Add review-time checks for comment-essay and CHANGELOG crispness
(pr-code-reviewer #30/#31, review-pr SKILL) and codify both in CLAUDE.md and
copilot-instructions.
Verified live: setup completes cleanly with no PowerShell block, and
query-entra reports the Observability app role granted on the blueprint SP with
effective inheritance OK.
Copy file name to clipboardExpand all lines: .claude/agents/pr-code-reviewer.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1122,6 +1122,26 @@ When a string property represents a discrete set of values (e.g., `"obo"`, `"s2s
1122
1122
```
1123
1123
-**Real example (PR #406, Comment 1)**: `isShowSecret = args.Contains("--show-secret")` in `Program.cs` would not skip the Graph preflight when the user passed `--show-secret=true`, accidentally triggering an online call on an offline-only command.
1124
1124
1125
+
### 30. Comment Essay — Multi-Line Rationale Block Where One Line Suffices
1126
+
1127
+
Comments added in the diff must be crisp. A comment states *why* in one or two lines; it does not retell the change, restate what the code already says, or narrate design history (that belongs in the commit message / PR description).
1128
+
1129
+
-**Pattern to catch**: any newly-added comment block — `//` inline or `///` XML `<summary>` — running more than ~2 lines of prose rationale; comments that paraphrase the next statement; commit-message-style narration ("Previously we did X, now we do Y because...") in source.
1130
+
-**Severity**: `low` — not a runtime defect, but the repo standard. Flag every instance so it does not accumulate.
1131
+
-**Check**: for each added comment, ask "does this say *why* in one line?" If it explains mechanism the code already shows, or runs to a paragraph, it is an essay. Trivial mechanical edits (a log-level change, a blank-line separator, a `catch { throw; }`) get **zero or one** line, never a rationale block.
1132
+
-**Fix**: cut to a single-line *why*; move the long-form reasoning to the commit message or PR body. Keep an issue/PR reference (`(issue #460)`) but drop the surrounding paragraph.
1133
+
-**Real example (issue #460)**: a 7-line `<summary>` and a 6-line inline `// Issue #460: ...` block both restated the inheritance rationale already captured in the commit; each was cut to one line plus the issue reference.
1134
+
1135
+
### 31. CHANGELOG Entry Not Release-Note-Ready
1136
+
1137
+
`CHANGELOG.md``[Unreleased]` feeds straight into the nuget.org release notes, so each entry must be **one crisp sentence about the user-visible change** — readable by a package consumer who has never seen the code.
1138
+
1139
+
-**Pattern to catch**: a new/edited `CHANGELOG.md` entry that names internal classes/methods (`BatchPermissionsOrchestrator`, `Phase 2a`), explains implementation mechanism (`retries via az rest`, `POST /appRoleAssignments`), restates rationale, or runs to multiple sentences of background.
1140
+
-**Severity**: `low`.
1141
+
-**Check**: read each CHANGELOG line in the diff. Would a consumer who only runs the CLI understand the *behavior* change from it, with no code knowledge? If it leans on internals or reads like a design note, it fails. Also verify the entry does not contradict another entry in the same `[Unreleased]` block (stale claims left behind by the change).
1142
+
-**Fix**: rewrite to the user-facing outcome in one sentence; keep the `(#NNN)` reference. Drop class/method names and mechanism. Correct any sibling entry the change makes stale.
1143
+
-**Real example (issue #460)**: a two-sentence entry naming `az rest` / PowerShell internals was cut to one outcome-focused sentence; a stale sibling line ("Phase 2a/2b always skipped for non-DW agents") that the change contradicted was corrected at the same time.
1144
+
1125
1145
## Example Invocation
1126
1146
1127
1147
When you receive a request like "Review PR #253", you should:
Copy file name to clipboardExpand all lines: .claude/skills/review-pr/SKILL.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -63,6 +63,8 @@ This skill enforces the following principles:
63
63
-**Function reuse**: Encourages reusing functions across commands
64
64
-**No special characters**: Avoids emojis in logs/output (Windows compatibility)
65
65
-**Self-documenting code**: Prefers clear code over excessive comments
66
+
-**Crisp comments (pr-code-reviewer #30)**: Flags added comments that run past 1-2 lines, restate the code, or narrate design history — a comment says *why* in one line; long-form reasoning belongs in the commit/PR.
67
+
-**Release-note-ready CHANGELOG (pr-code-reviewer #31)**: Flags `CHANGELOG.md` entries that name internals, explain mechanism, or run multiple sentences — each entry is one crisp consumer-facing sentence (it ships verbatim to nuget.org release notes).
66
68
-**Minimal changes**: Makes only necessary changes to solve the problem
Copy file name to clipboardExpand all lines: .github/copilot-instructions.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -100,6 +100,13 @@
100
100
-Keepuser-facingmessagesclearandprofessional
101
101
-Followclient-facinghelptextconventions
102
102
103
+
### Comments
104
+
-Commentsarecrisp:state*why*inoneortwolines, neveranessay. A `//` or `///` `<summary>` that runs to a paragraph, restates the code, or narrates the change ("previously X, now Y because...") belongs in the commit message / PR, not in source.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -37,7 +37,7 @@ Agents provisioned before this release need `Agent365.Observability.OtelWrite` g
37
37
-`obo` (default): principal-scoped delegated grants (`consentType: "Principal"`); no Global Administrator required.
38
38
-`s2s`: application role assignments on the agent identity SP; attempted programmatically, falls back to printed PowerShell instructions if the caller lacks Global Administrator.
39
39
-`both`: applies both OBO delegated grants and S2S app role assignments.
40
-
-Inheritable permissions (Phase 2a) and AllPrincipals grants (Phase 2b) are always skipped for non-DW agents regardless of `authMode`, to avoid requiring a Global Administrator role.
40
+
-Non-DW agents stamp inheritable permissions and S2S grants on the blueprint (Global Administrator only); the agent identity inherits them, so a per-identity grant runs only when inheritance is not in force.
41
41
-`authMode` can be persisted in `a365.config.json` to apply on every run without the flag.
42
42
-`--project-path <path>` option on `develop list-configured`, `develop add-mcp-servers`, and `develop remove-mcp-servers` — specify the manifest location without requiring `a365.config.json`.
43
43
-`setup requirements` runs without `a365.config.json` — system checks (PowerShell modules, Frontier enrollment) always run; client app checks run when a config file or Azure CLI session is available.
@@ -58,6 +58,7 @@ Agents provisioned before this release need `Agent365.Observability.OtelWrite` g
58
58
-`a365 develop get-token --device-code` — forces device code auth for Microsoft Graph scopes the Windows WAM broker rejects (e.g. Exchange `MailboxSettings.ReadWrite`, `ExchangeMessageTrace.Read.All`).
59
59
60
60
### Fixed
61
+
-`setup all --authmode s2s` no longer prints spurious "Action Required" PowerShell steps when the agent identity already inherits its app roles from the blueprint, and now retries the grant automatically before falling back to manual steps (#460).
61
62
-`a365 develop get-token` now falls back to device code when the Windows WAM broker rejects Exchange Graph scopes with `ApiContractViolation`, instead of failing with an opaque MSAL error.
62
63
-`setup blueprint` now configures the blueprint's inheritable Microsoft Graph permissions even when the signed-in user is not a Global Administrator, no longer aborts with a misleading "Failed to configure inheritable permissions" error when the tenant-wide consent grant cannot be made programmatically, and ends with a setup summary whose Action Required block surfaces the admin-consent URL for non-admins to hand off (#452).
63
64
- Messaging endpoint registration and removal now retry on transient network errors instead of failing on a momentary DNS or connection blip.
Comments are crisp: state *why* in one line, not *what* the code already shows. Do not write essays.
116
+
- A code comment (`//` or `///``<summary>`) is **one or two lines**. If you need a paragraph of rationale, it belongs in the commit message or PR description, not in source.
117
+
- Keep an issue/PR reference (`(issue #460)`) but drop the surrounding narration.
118
+
- Never narrate the change ("previously we did X, now Y because...") — that is commit-message content.
119
+
- Trivial mechanical edits (log-level change, blank-line separator, `catch { throw; }`) get zero or one line, never a rationale block.
120
+
121
+
```csharp
122
+
// Bad: essay restating the change and its history
123
+
// Issue #460: when inheritable permissions were configured with kind=allAllowed (covering both
124
+
// scopes and roles) AND the blueprint SP was granted the app roles, the agent identity inherits
125
+
// them automatically — the same basis the OBO branch relies on. A direct grant then only adds a
126
+
// duplicate row and a spurious prompt, so we skip it and report the inherited grant as Granted.
127
+
128
+
// Good: one-line why + reference
129
+
// Skip the direct grant when the role is already inherited from the blueprint (issue #460).
130
+
```
131
+
132
+
### CHANGELOG entries
133
+
`CHANGELOG.md``[Unreleased]` ships verbatim to nuget.org release notes. Each entry is **one crisp consumer-facing sentence** about the user-visible change — no class/method names, no implementation mechanism, no multi-sentence rationale. Keep the `(#NNN)` reference. When a change makes a sibling entry stale, fix it in the same edit.
134
+
114
135
### Input Validation
115
136
User-controlled input that reaches file system operations must be validated before use. This applies to CLI arguments, config values read from disk, and any value whose origin is outside this process.
|**NonDwBlueprintSetupOrchestrator**|`NonDwBlueprintSetupOrchestrator.cs`| Blueprint-based non-DW setup flow; stamps inheritable permissions + S2S grants on the blueprint, then gates agent identity grants by `authMode` (skipping the per-identity S2S grant when the role is already inherited)|
25
25
26
26
---
27
27
@@ -74,12 +74,12 @@ The `--authmode` option controls how the agent identity service principal is gra
74
74
| Value | Behaviour |
75
75
|-------|-----------|
76
76
|`obo` (default) | Principal-scoped delegated grants (`consentType: "Principal"`) on the agent identity SP — no Global Admin required |
77
-
|`s2s`| Application role assignments on the agent identity SP — attempted programmatically; PowerShell instructions printed as fallback if the caller lacks Global Admin|
77
+
|`s2s`| Application role assignments — when the blueprint already holds the roles and inheritable permissions are configured (`allAllowed`), the agent identity inherits them and no direct grant is made; otherwise the grant is attempted on the agent identity SP programmatically, then via `az rest`, with PowerShell instructions printed only if both fail|
78
78
|`both`| Both OBO delegated grants and S2S app role assignments |
79
79
80
80
`authMode` may also be persisted in `a365.config.json` so it takes effect on every run without the flag.
81
81
82
-
Phase 2a (inheritable permissions on the blueprint) and Phase 2b (AllPrincipals grants) are **always skipped** for non-DW agents regardless of `authMode`, to avoid requiring a Global Admin role.
82
+
Non-DW agents stamp the same permission spec set on the blueprint (inheritable permissions with `kind=allAllowed`, plus S2S app-role grants on the blueprint SP when the caller is a Global Admin). Because inheritance covers both scopes and roles, the agent identity inherits the blueprint's grants automatically — so the per-identity S2S grant (issue #460) is skipped when the blueprint grant and inheritance both succeeded, and the delegated (OBO) path never issues a per-identity grant at all.
0 commit comments