mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-10-25 13:11:05 +00:00
docs/tutorials-batch-3
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b689733447 |
refactor(gitops): one layered status across the Drift tab, Git source sheet, and application view (#2063)
* refactor(gitops): one layered status across the Drift tab, Git source sheet, and application view The GitOps state used to be a stack of tinted cards on five surfaces: a posture card, approval chips, a card per facet, a card per target, and a caveats box, restating the same state several times. Each surface now shows one status: a single answer (with its resolving button, such as Review update or Retry), a quiet row naming each stage that applies, and an Evidence control holding recorded authority, policy lines, targets, and anything Sencho could not prove. - Stages that say nothing about an application (unbound placement, an absent rollout) are left out of the row instead of printed as "n/a". - Evidence is never silently lost: the answer carries a marker for partial evidence, unreached nodes, and unproven items, and every attention reason is kept, the first in the answer and the rest in the evidence. - The Git source sheet's Status section drops from about 450px to about 170px, and its separate source-state and last-applied rows go; the header now says "never applied" until something is. - The application view no longer shows the posture twice. Adds a domain-free StatusPath primitive, a pure status model, and a GitOpsStatus composer; retires the facet-card and approval-chip components and keeps the policy lines. Docs, the tutorial, and the two affected screenshots are updated. * test(gitops): open the status evidence in the digest drift spec The per-service digests now sit under the Evidence control of the GitOps status, so the spec opens it before looking for them. |
||
|
|
c92004dc6a |
fix(blueprints): retry a refused drift repair instead of stranding the row (#2028)
* fix(blueprints): retry a refused drift repair instead of stranding the row A drift repair that could not finish left the deployment row reading `correcting` forever, so nothing ever retried it. `drift_enforce_start` writes `correcting` before the repair runs, and a repair that then answered without ever reaching a deploy wrote no status of its own. That state is in-flight as far as the decision is concerned, so `computeDecision` skipped it and the preview projection rendered it as the informational `in_flight_correct`. No later tick re-checked the node: the workload kept running the moved image, one correction-failed alert fired, and then nothing, for as long as the drift lasted. Four ticks on a refused repair recorded one drift check, one repair attempt and one alert, and did nothing on the rest. A refusal, or a repair that throws, now settles the row back to `drifted` with the reason in `last_error`, so the next tick checks and retries it. A throw counts because resolving a remote node's platform label is a network call made before any deploy, so it can reject with nothing written and used to escape to the per-blueprint catch with the row still `correcting`. The row deliberately does not become `failed` or `pending`. Both are retry buckets for an ordinary deploy, so the next tick would repair by tag and adopt whatever the tag points at now, which is the one thing a digest-pinned repair exists to prevent. The failure alert fires when the row changed across the attempt, compared against a snapshot read before the repair ran. It cannot be decided from the row afterwards: a repair that reaches a deploy writes its own `last_error` and returns the very same string, so asking the row afterwards finds the failure already sitting there and mistakes a new incident for a repeat, dropping the first failure of every kind silently. Losing the deploy lock is a deferral rather than a failure, so the row still leaves `correcting` with a reason and the operator is not paged about a race they cannot act on. A converged row now forgets its last failure, as the deploy acknowledgement already did, so a failure that returns after a repair counts as a new incident. computeDecision also treats a `drifted` row at the current revision as a drift-check target. The projection already reached it through its own status fallback with the same action, severity and confirmable membership, so nothing an operator sees changes; it is here so the decision a tick acts on is not two files away in the preview projection. Verified: 10333 backend tests pass with no failures, typecheck and lint clean, and an e2e case drives drift, repair and reconvergence on a real Inline Blueprint, asserting the row returns to Active with no drift summary and the workload runs the approved digest again. * fix(blueprints): correct the drift repair settle found in review Four corrections to the refused-repair settle, all found by reviewing the change rather than by a failing test. The deferral note was the serious one. Losing the deploy lock is silent, and it wrote a note about the race into `last_error`, which is the replay key. One lost-lock tick between two identical failures therefore displaced the failure being replayed, and the next tick reported that same known failure again as though it were new. A deferral now leaves `last_error` alone: the row keeps saying what it last actually failed with, which is worth more to an operator than a note about a race that is already over. An alert now quotes the row rather than the outcome where the outcome carries only a shorthand. The name-conflict paths return the bare word `name_conflict` and then park the row for good, so that one alert is the only signal the operator will ever get, and it has to name the directory in the way. The row's own text is only preferred when the repair actually wrote a status: when the row is still `correcting`, nothing was written and the row is still carrying the previous failure, which must not be reported as the current one. A target that converges while its repair is in flight is no longer reported. The drift the failure belonged to no longer exists, so paging the operator about it is stale news. A repair that throws is now logged with its stack as well as alerted. This boundary cannot tell an operational failure from a bug in the repair and both arrive identically; an alert alone would repeat a bug every tick and never reach a developer, and re-throwing would strand the row again. Separately, the drift observation restamped `last_drift_at` every time it re-observed the same drift, so a drift outliving one tick reported itself as just found, forever. It is stamped only when the row was not already drifted. Both observation sites did this, the Inline one and the Git-managed one. Coverage added for the Git-managed repair path, which is the third dispatch and had no case, for the timestamp, and for the three alert rules above. 19 cases now, 5 of which fail with the whole change reverted. |
||
|
|
61b25f48d2 |
feat(gitops): show per-target image digests and document the drift model (#1984)
* feat(gitops): show per-target image digests and document the drift model The observed per-service digests already reached the projection, but the approved side did not, so a reader could see that two identities differed without ever seeing which digest each side held. The frozen artifact set's per-service evidence now rides along with the expected identity, and each target's card compares them per service: the approved digest beside the running one, the approved child for the platform that node actually runs, and matched, drifted, unverified, or not compared as the honest answer. The comparison follows the model's own rules, so the view cannot claim a match the projection does not, a stale or unqualified identity stays unverified, a built service is labelled not compared rather than matched, and a value that is not a content digest is ignored instead of compared. Adds the drift model reference the model was missing: the seven classes and what each is measured against, how to read an item, where each surface shows it, the states that are progress rather than drift, what Sencho cannot prove, and the four policy domains. The Blueprint page gains the digest-drift section it never had, trimmed to the Enforce mechanics. The end-to-end spec pins an approved digest, replaces the running workload with different content under the same project, and asserts the deployment reports the drift, the projection carries both digests, and the Drift tab renders the comparison without adopting what the tag now points at. * test(gitops): pin the digest drift spec to a resolvable repository The spec pinned a locally invented repository, e2e-digest-probe:<stamp>@sha256:..., which is not a reference any registry knows. That works or fails depending on what the local Docker version decides to do with a tag-and-digest reference that has no matching repo digest, and the observation filters the running image's repo digests by the authored repository, so an invented repository can never produce an observed digest at all. On a runner whose Docker resolves it differently, the deploy never lands and the spec waits out its poll. Both sides are now real upstream tags of one repository, so the approved reference resolves from the local content store without a registry call and the moved side is a real digest difference. The digests are read from the daemon per repository rather than trusting Docker's ordering, and the two sets are asserted disjoint before anything runs, so two tags that resolve to one manifest fail by name instead of by timeout. The spec also stopped assuming things it never checked. The baseline is proven from the daemon, because the container that was actually created from the approved reference is the ground truth for what is running, and reading it costs one call where waiting for the projection's observation costs a reconciler tick. Every poll now asserts its status, so an API error reports itself instead of surfacing as a timeout, the apply retry is limited to the stale-preview case the UI asks the operator to confirm again, Docker calls are bounded, teardown clears its handle only after a confirmed delete and then checks the containers are actually gone, and a Docker that is installed but broken fails instead of skipping. Verified against local dev servers across four runs at 16 to 55 seconds each, with no leftover images, containers, or override files, and type-clean under a strict invocation. * test(gitops): prove Enforce repairs to the approved digest, not the moved tag The digest drift spec proves detection. It does not prove repair, because it runs the Blueprint in observe mode, and the Inline path is not the one that repairs. The Git-managed loop re-observes a drifted target and applies the configured drift mode, so that is where the repair mechanic lives, and it now has a test of its own. The test starts from a genuinely drifted target rather than an assumed one. The fixture seeds a newer candidate artifact set carrying a moved digest, points the latest pointer at it while the approved and last-known-good pointers stay on the original, and records a runtime observation carrying the moved digest. That starting state is asserted before repair runs, so a fixture that quietly stopped seeding it fails instead of passing on a correct-looking end state. Repair then has to do the one thing that matters: hand the deploy the approved platform child digest, take it from the accepted artifact set rather than the most recent resolution or the running image, and leave every pointer that says what was approved exactly where it was. The observation still reads as drifted afterwards, because repair changes what runs, not what was agreed. The test is mutation-checked. Pointing repair at the latest artifact set instead of the expected one makes it fail with the moved digest, which is the bug this exists to catch. The taxonomy page also records the settings-only reconcile decision properly: source drift compares commits, so a change of compose path, env path, or context directory creates no expected-versus-observed pair and no drift item. It surfaces as reconcile required, and for a Direct application that is where you fetch again. The page says plainly that this is a choice about what the word drift means, not proof that the materialized content was unchanged, because changing where Compose is read from can change the content. The portfolio page no longer keeps its own copy of the class list. It names the classes by reference to the drift model page, so the two cannot disagree. Verified: backend typecheck and lint clean, 9515 backend tests passing, frontend typecheck clean with zero lint errors and 3457 tests across 347 files, docs config parses. * test(gitops): pin a settings-only reconcile as attention, not drift The decision is recorded on the taxonomy page: a change to the compose path, env path, or context directory, with the accepted commit still matching the ref, creates no source drift item. Source drift compares commits, and that shape has no expected-versus-observed pair of commits. That decision needed a test to hold it, and the coverage that existed asserted the facet status and the offered action without ever asserting that no source item is emitted. The behavior was therefore unpinned in both directions: nothing would have failed if the producer started reporting it, and nothing would have failed if it stopped. This adds the missing half. The fixture is the decided case exactly: same commit, different materialization fingerprint. No source drift item, the facet reads reconcile required, and fetch is offered. It is mutation-checked, and dropping the commit comparison from the producer makes it fail, so it holds the line rather than merely restating it. |