Repository navigation
phase2-attest-baseline: drift-aware --baseline diff for azureclaw attest (Phase 2 S11.1) - #61
Merged
Conversation
…test`
Outcome-shaped follow-up to S11. Turns `azureclaw attest` from "print
JSON" into a CI-gate / change-control primitive: pass --baseline
<file> and the command compares the live sandbox against a previously-
saved attestation, surfaces typed deltas, and exits 2 on drift / 3 on
missing baseline so a pipeline step can `set -e` against it.
Real workflow this unlocks:
# Day 0 — capture approved posture
azureclaw attest demo --format json > approved.json
git add approved.json && git commit -m "approved: demo posture"
# Every PR / nightly job — fail the build on drift
azureclaw attest demo --baseline approved.json || exit $?
What deltas are surfaced (one human-meaningful change per delta):
- specHash — ClawSandbox.spec changed
- phase — Running ↔ Overlay ↔ Degraded
- policyVersionHash — referenced policy CR recompiled
- policyAdded/Removed — spec now references a different policy set
- fieldOwnerAdded/Removed — new (or removed) SSA manager touched
the object since baseline
Set-comparison, not count-comparison, on field owners: SSA bumps the
per-field count on every controller reconcile (noisy), but the set of
managers is what a CI gate actually wants to flag — "did a human or a
tool that wasn't here before edit this object?". Asserted in tests.
Pure-function design: diffAttestations(baseline, current) is the only
new logic; no IO, no time, no kubectl. Means a future Phase 3
`azureclaw verify <bundle>` companion can reuse it unchanged.
Exit codes:
0 — match
2 — drift (deltas reported)
3 — baseline file missing (printed to stderr before any kubectl)
JSON output grows a `baselineDiff: { baseline, current, deltas, drift }`
field. Base envelope unchanged so existing consumers continue parsing.
Tests: CLI workspace 304 → 315 (+11). 11 new cases covering every
delta variant, set-comparison vs count-fluctuation invariant, missing
baseline, invalid baseline, exhaustive describeDelta. tsc --noEmit +
vitest + oxlint green; ci/no-stubs.sh + ci/no-custom-crypto.sh +
ci/check-loc.sh green with BASE_REF=origin/dev.
Audit: docs/security-audits/2026-04-28-phase2-attest-baseline.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pal Lakatos-Toth (pallakatos)
added a commit
that referenced
this pull request
May 12, 2026
…test` (#61) Outcome-shaped follow-up to S11. Turns `azureclaw attest` from "print JSON" into a CI-gate / change-control primitive: pass --baseline <file> and the command compares the live sandbox against a previously- saved attestation, surfaces typed deltas, and exits 2 on drift / 3 on missing baseline so a pipeline step can `set -e` against it. Real workflow this unlocks: # Day 0 — capture approved posture azureclaw attest demo --format json > approved.json git add approved.json && git commit -m "approved: demo posture" # Every PR / nightly job — fail the build on drift azureclaw attest demo --baseline approved.json || exit $? What deltas are surfaced (one human-meaningful change per delta): - specHash — ClawSandbox.spec changed - phase — Running ↔ Overlay ↔ Degraded - policyVersionHash — referenced policy CR recompiled - policyAdded/Removed — spec now references a different policy set - fieldOwnerAdded/Removed — new (or removed) SSA manager touched the object since baseline Set-comparison, not count-comparison, on field owners: SSA bumps the per-field count on every controller reconcile (noisy), but the set of managers is what a CI gate actually wants to flag — "did a human or a tool that wasn't here before edit this object?". Asserted in tests. Pure-function design: diffAttestations(baseline, current) is the only new logic; no IO, no time, no kubectl. Means a future Phase 3 `azureclaw verify <bundle>` companion can reuse it unchanged. Exit codes: 0 — match 2 — drift (deltas reported) 3 — baseline file missing (printed to stderr before any kubectl) JSON output grows a `baselineDiff: { baseline, current, deltas, drift }` field. Base envelope unchanged so existing consumers continue parsing. Tests: CLI workspace 304 → 315 (+11). 11 new cases covering every delta variant, set-comparison vs count-fluctuation invariant, missing baseline, invalid baseline, exhaustive describeDelta. tsc --noEmit + vitest + oxlint green; ci/no-stubs.sh + ci/no-custom-crypto.sh + ci/check-loc.sh green with BASE_REF=origin/dev. Audit: docs/security-audits/2026-04-28-phase2-attest-baseline.md. Co-authored-by: Pal Lakatos-Toth <pallakatos@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Outcome-shaped follow-up to S11 (#59). Turns
azureclaw attestfrom "print JSON" into a CI-gate / change-control primitive.Real-world workflow this unlocks
Surface
cli/src/commands/attest.tsaddsdiffAttestations(pure function),loadBaseline,describeDelta,--baselineflag, exit-code handling.cli/src/commands/attest.test.tsadds 11 new cases (every delta variant, set-vs-count invariant, missing/invalid baseline, exhaustivedescribeDelta).CHANGELOG.md— S11.1 entry above S11.docs/security-audits/2026-04-28-phase2-attest-baseline.md— 11-section audit (reuse, STRIDE, out-of-scope, set-vs-count rationale).Deltas surfaced (one variant per human-meaningful change)
specHashClawSandbox.specitself changedphasepolicyVersionHashpolicyAdded/policyRemovedfieldOwnerAdded/fieldOwnerRemovedSet-comparison, not count-comparison, on field owners. SSA bumps the per-field count on every controller reconcile (noisy); set comparison is the right granularity to flag "did a new actor touch this object?" without paging on every reconcile. Asserted in tests.
Exit codes (CI-friendly)
0— match2— drift (deltas reported in human + JSON output)3— baseline file missing (stderr before any kubectl)Pure-function design
diffAttestations(baseline, current)has no IO, no time, no kubectl. A future Phase 3azureclaw verify <bundle>companion can reuse it unchanged.Reuse, no duplication (§0.2 #11)
AttestationReportshape; baseline file is the S11 attestation JSON envelope. No second schema.node:fs/promisesalready in CLI).Out of scope (Phase 3)
azureclaw verify <bundle>companion (will reusediffAttestations).--at <ts>time-travel mode (needs controller-side persistent receipt log).--all/--baseline-dirfleet mode (separate slice).Verification
cd cli && npx tsc --noEmit✅cd cli && npm test— 315 passed | 2 skipped (was 304+2; +11 from this slice) ✅cd cli && npm run lint✅ (preexisting warnings only)BASE_REF=origin/dev bash ci/no-stubs.sh✅BASE_REF=origin/dev bash ci/no-custom-crypto.sh✅BASE_REF=origin/dev bash ci/check-loc.sh✅Phase 2 progress on dev after merge: ✅ S1 #51, S2 #52, S3 #53, S4 #54, S5 #55, S6 #56, S8 #57, S11 #59, S11.1 (this PR) — 9 of ~14 slices.