carlos run logs: run lines are at most once; a restart's close-out logs only after its own done CAS (#325, Astra round 2)
Human
Prompt
re-up copy review?
From this commit's own message. The prompt ledger has no entries for it — which is what a squashed, rebased or amended commit looks like, and what every commit looks like in a repo without a ledger.
Summary
Astra round 2: closeInterrupted now logs the ending only when this pass wrote the result AND its done CAS landed, so a stale pass after an ack (result deleted, record cleared) logs no duplicate. Policy: run lines are at most once and best effort like the rest of the platform stream; result.json and the audit log are the record, so a lost answer loses the line. Launch-window cancel/timeout keeps 'before it started', restating the result as the CLI does (documented residual: the result does not record whether StartCmd was reached).
5 files changed,
+61
−21
internal/host/runevents.go+5 −1internal/host/runevents_test.go+37 −5internal/host/runs.go+16 −12internal/provision/plan9_b8_census_test.go+2 −2internal/provision/testdata/plan14-root-reads-run-lifecycle-logs-325.tsv+1 −1
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.011s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok github.com/carlosframework/platform 316.269s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.742s
ok github.com/carlosframework/platform/cmd/moveprobe 93.169s
ok github.com/carlosframework/platform/cmd/zddprobe 0.703s
ok github.com/carlosframework/platform/internal/activator 251.395s
ok github.com/carlosframework/platform/internal/agentsock 0.014s
ok github.com/carlosframework/platform/internal/alerting 0.004s
ok github.com/carlosframework/platform/internal/analytics 0.019s
ok github.com/carlosframework/platform/internal/appconfig 2.459s
ok github.com/carlosframework/platform/internal/apperr 0.070s
ok github.com/carlosframework/platform/internal/applog 0.126s
ok github.com/carlosframework/platform/internal/awsinv 0.003s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.726s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.008s
ok github.com/carlosframework/platform/internal/caps 0.012s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.693s
ok github.com/carlosframework/platform/internal/changelog 0.336s
ok github.com/carlosframework/platform/internal/claimactivation 0.757s
ok github.com/carlosframework/platform/internal/clamd 0.054s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 409.178s
? github.com/carlosframework/platform/internal/controlname [no test files]
? github.com/carlosframework/platform/internal/controlsock [no test files]
ok github.com/carlosframework/platform/internal/dbdoor 5.902s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.009s
ok github.com/carlosframework/platform/internal/dbverify 0.005s
ok github.com/carlosframework/platform/internal/dnsrec 0.003s
ok github.com/carlosframework/platform/internal/docsite 0.006s
ok github.com/carlosframework/platform/internal/domains 2.349s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.004s
ok github.com/carlosframework/platform/internal/edge 44.978s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.133s
ok github.com/carlosframework/platform/internal/gate 1.655s
ok github.com/carlosframework/platform/internal/genselect 0.015s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.030s
ok github.com/carlosframework/platform/internal/historyreadback 0.152s
ok github.com/carlosframework/platform/internal/host 180.778s
ok github.com/carlosframework/platform/internal/hostactors 5.975s
ok github.com/carlosframework/platform/internal/hostactors/awsmembership 0.011s
ok github.com/carlosframework/platform/internal/hostbox 2.707s
ok github.com/carlosframework/platform/internal/hostclaim 0.553s
ok github.com/carlosframework/platform/internal/hostenv 0.011s
ok github.com/carlosframework/platform/internal/hostevidence 0.011s
ok github.com/carlosframework/platform/internal/hostexec 4.913s
ok github.com/carlosframework/platform/internal/hostfence 0.031s
ok github.com/carlosframework/platform/internal/hostfiles 10.577s
ok github.com/carlosframework/platform/internal/hostfloor 0.016s
ok github.com/carlosframework/platform/internal/hostguard 0.993s
ok github.com/carlosframework/platform/internal/hostops 1.598s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.010s
ok github.com/carlosframework/platform/internal/hostquarantine 0.011s
ok github.com/carlosframework/platform/internal/hostrelocate 0.451s
ok github.com/carlosframework/platform/internal/hostresolve 0.149s
ok github.com/carlosframework/platform/internal/hostwrite 0.010s
ok github.com/carlosframework/platform/internal/infraview 0.010s
ok github.com/carlosframework/platform/internal/instancectl 5.236s
ok github.com/carlosframework/platform/internal/instanceexec 2.765s
ok github.com/carlosframework/platform/internal/instancefs 0.015s
ok github.com/carlosframework/platform/internal/instancekey 0.014s
ok github.com/carlosframework/platform/internal/instanceupdates 0.018s
ok github.com/carlosframework/platform/internal/layout 1.533s
ok github.com/carlosframework/platform/internal/lbhealth 0.002s
ok github.com/carlosframework/platform/internal/lease 9.864s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.007s
ok github.com/carlosframework/platform/internal/ledger 0.019s
ok github.com/carlosframework/platform/internal/mail 0.167s
ok github.com/carlosframework/platform/internal/maintenance 0.078s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.536s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.085s
ok github.com/carlosframework/platform/internal/metrics 0.006s
ok github.com/carlosframework/platform/internal/moves 21.050s
ok github.com/carlosframework/platform/internal/moves/archive 0.804s
ok github.com/carlosframework/platform/internal/objstore 0.177s
ok github.com/carlosframework/platform/internal/ops 0.169s
ok github.com/carlosframework/platform/internal/park 0.192s
ok github.com/carlosframework/platform/internal/privatefile 0.003s
ok github.com/carlosframework/platform/internal/provision 36.932s
ok github.com/carlosframework/platform/internal/publicrelease 0.008s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.479s
ok github.com/carlosframework/platform/internal/relay 7.244s
ok github.com/carlosframework/platform/internal/release 24.513s
ok github.com/carlosframework/platform/internal/rollout 3.826s
ok github.com/carlosframework/platform/internal/rolloutwords 0.003s
ok github.com/carlosframework/platform/internal/rootaws 0.006s
ok github.com/carlosframework/platform/internal/rootparticipation 0.058s
ok github.com/carlosframework/platform/internal/rootstate 0.026s
ok github.com/carlosframework/platform/internal/runs 0.578s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.524s
ok github.com/carlosframework/platform/internal/seeds 0.011s
ok github.com/carlosframework/platform/internal/sim/hostlife 9.788s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 4.539s
ok github.com/carlosframework/platform/internal/status 0.058s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.235s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.004s
ok github.com/carlosframework/platform/internal/unixdial 0.003s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.004s
ok github.com/carlosframework/platform/internal/version 0.002s
ok github.com/carlosframework/platform/internal/vet 1.591s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 49414306 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
minio: downloaded, digest verified
mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
mc: downloaded, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.03s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.02s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.035s
=== RUN TestT4StorageProbeMinio
2026/10/20 14:54:35 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E0449903781ECD, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok github.com/carlosframework/platform/internal/host 0.024s
minio contract: PASS
diff --git a/internal/host/runevents.go b/internal/host/runevents.go| index 2aeebfdfc..338498311 100644 |
| --- a/internal/host/runevents.go |
| +++ b/internal/host/runevents.go |
| @@ -32,7 +32,11 @@ func runStartedLine(req runs.Request) string { |
| // its fields say. A failed start is the exception: StartCmd can answer an |
| // error for a unit the control service did start (a lost response), and |
| // RunCmd then stops it and hands back nothing, so the line hedges as the |
| -// CLI's does rather than say it never ran. |
| +// CLI's does rather than say it never ran. A cancel or a deadline that |
| +// lands during that same start is written as cancelled or timed out with |
| +// no handle, and its line says "before it started", as the CLI says of the |
| +// same result: the result does not record whether the start was reached, |
| +// and the line restates the result, never more. |
| func runEndedLine(id string, res runs.Result, started bool) string { |
| took := res.Ended.Sub(res.Started).Round(time.Second) |
| switch { |
diff --git a/internal/host/runevents_test.go b/internal/host/runevents_test.go| index 955fb5fd4..3375c4e4f 100644 |
| --- a/internal/host/runevents_test.go |
| +++ b/internal/host/runevents_test.go |
| @@ -144,9 +144,38 @@ func TestRunClosedOutAfterItsOwnResultLogsNothing(t *testing.T) { |
| } |
| } |
| -// A done CAS whose answer was lost still leaves the ending logged once: |
| -// it is logged with the result, not with the CAS (Astra review, finding 3). |
| -func TestRunClosedOutWithALostDoneAnswerIsLoggedOnce(t *testing.T) { |
| +// A stale pass after the run was acknowledged (its result deleted, the |
| +// record cleared) recreates a result, but its done CAS fails, so it logs |
| +// no second ending (Astra round 2, finding 2). |
| +func TestRunClosedOutByAStalePassAfterTheAckLogsNothing(t *testing.T) { |
| + s := release.NewDirStore(t.TempDir()) |
| + _, id := queueRun(t, s, time.Now().Add(time.Minute)) |
| + got, err := provision.SetRecordRun(context.Background(), s, "bab", "shop", runHost, provision.RunPick(id, "box-1", "boot-old", time.Now().Add(time.Hour))) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + var l execLog |
| + var events eventLog |
| + w := testWorker(s, runOwnedRoute(runHost), "box-1", "boot-new", &l) |
| + w.events = &events |
| + w.consider(context.Background(), *got) |
| + // The CLI acknowledges the run: the record is cleared and the result |
| + // deleted, as handleAPIRunAck does. |
| + if _, err := provision.SetRecordRun(context.Background(), s, "bab", "shop", runHost, provision.RunClear(id)); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := runs.DeleteRun(context.Background(), s, "bab", "shop", id); err != nil { |
| + t.Fatal(err) |
| + } |
| + w.consider(context.Background(), *got) |
| + if ev := events.all(); len(ev) != 1 { |
| + t.Fatalf("events %+v, want the first pass's ending only", ev) |
| + } |
| +} |
| + |
| +// A done CAS whose answer was lost loses the line rather than risk a |
| +// second one: run lines are at most once, and result.json is the record. |
| +func TestRunClosedOutWithALostDoneAnswerIsNotLoggedTwice(t *testing.T) { |
| dir := release.NewDirStore(t.TempDir()) |
| _, id := queueRun(t, dir, time.Now().Add(time.Minute)) |
| got, err := provision.SetRecordRun(context.Background(), dir, "bab", "shop", runHost, provision.RunPick(id, "box-1", "boot-old", time.Now().Add(time.Hour))) |
| @@ -162,8 +191,11 @@ func TestRunClosedOutWithALostDoneAnswerIsLoggedOnce(t *testing.T) { |
| if r := currentRun(t, dir); r.State != provision.RunDone { |
| t.Fatalf("run %+v, want done (the CAS landed)", r) |
| } |
| - if ev := events.all(); len(ev) != 1 || !strings.Contains(ev[0].msg, "the box's agent restarted") { |
| - t.Fatalf("events %+v, want one interrupted ending", ev) |
| + if ev := events.all(); len(ev) != 0 { |
| + t.Fatalf("events %+v, want none: the done answer was lost", ev) |
| + } |
| + if res, _ := runs.ReadResult(context.Background(), dir, "bab", "shop", id); res == nil || res.Interrupted != runs.InterruptedAgentRestart { |
| + t.Fatalf("result %+v, want the interrupted result", res) |
| } |
| } |
diff --git a/internal/host/runs.go b/internal/host/runs.go| index 44cd4c05d..f85eccefd 100644 |
| --- a/internal/host/runs.go |
| +++ b/internal/host/runs.go |
| @@ -314,19 +314,22 @@ func (w *runsWorker) closeInterrupted(ctx context.Context, rec provision.Record) |
| return |
| } |
| res := runs.Result{Interrupted: runs.InterruptedAgentRestart, Box: w.box, Ended: w.now().UTC(), LastSeq: last} |
| - // The ending is logged by the pass whose result is the run's, and by no |
| - // other (amadan #325). Every pass that still reads the run as picked |
| - // comes back here, and the create-only write answers a second time as |
| - // if it had landed; a result already there was written by the boot that |
| - // ran the command, which logged its own ending, or by an earlier pass |
| - // of this one. So the result is read first, and read back after the |
| - // write: a write whose answer was lost but whose result is this pass's |
| - // still logs, once. |
| + // A run's log lines are at most once and best effort (amadan #325), like |
| + // the rest of the platform stream; result.json and the audit log are its |
| + // record. So the ending is logged only by a pass that wrote the run's |
| + // result itself (read before, read back after) AND whose own done CAS |
| + // landed. Every pass that still holds a picked snapshot comes back here: |
| + // the create-only write answers a second time as if it had landed, a |
| + // result already there is the boot's that ran the command (which logged |
| + // its own ending), and a stale pass after the run was acknowledged |
| + // recreates a result but its CAS fails. A lost answer anywhere loses the |
| + // line rather than risk a second one. |
| prior, err := runs.ReadResult(ctx, w.store, rec.Account, rec.App, id) |
| if err != nil { |
| log.Printf("agent: runs: %s %s: read the result: %v", rec.Host, id, err) |
| return |
| } |
| + ours := false |
| if prior == nil { |
| werr := runs.WriteResult(ctx, w.store, rec.Account, rec.App, id, res) |
| got, err := runs.ReadResult(ctx, w.store, rec.Account, rec.App, id) |
| @@ -334,12 +337,13 @@ func (w *runsWorker) closeInterrupted(ctx context.Context, rec provision.Record) |
| log.Printf("agent: runs: %s %s: result: %v", rec.Host, id, errors.Join(werr, err)) |
| return |
| } |
| - if got.Box == res.Box && got.Ended.Equal(res.Ended) && got.Interrupted == res.Interrupted { |
| + ours = got.Box == res.Box && got.Ended.Equal(res.Ended) && got.Interrupted == res.Interrupted |
| + } |
| + if _, err := provision.SetRecordRun(ctx, w.store, rec.Account, rec.App, rec.Host, provision.RunFinish(id)); err == nil { |
| + if ours { |
| w.runEvent(rec, runEndedLine(id, res, true)) |
| } |
| - } |
| - if _, err := provision.SetRecordRun(ctx, w.store, rec.Account, rec.App, rec.Host, provision.RunFinish(id)); err != nil && |
| - !errors.Is(err, provision.ErrRunConflict) && !errors.Is(err, os.ErrNotExist) { |
| + } else if !errors.Is(err, provision.ErrRunConflict) && !errors.Is(err, os.ErrNotExist) { |
| log.Printf("agent: runs: %s %s: done: %v", rec.Host, id, err) |
| } |
| if err := runs.RemoveRunDir(provision.InstanceDir(w.dataDir, rec.Host), id); err != nil && !errors.Is(err, os.ErrNotExist) { |
diff --git a/internal/provision/plan9_b8_census_test.go b/internal/provision/plan9_b8_census_test.go| index 6774775c5..207be0618 100644 |
| --- a/internal/provision/plan9_b8_census_test.go |
| +++ b/internal/provision/plan9_b8_census_test.go |
| @@ -1105,8 +1105,8 @@ var plan9B8CensusInventory = []censusEntry{ |
| // Re-reviewed for run-lifecycle-logs-325 (amadan #325, proposed): emits the run's end as a member log event; record writes unchanged. |
| {"internal/host/runexec.go", "runsWorker.finish", "record-writer", "key:SetRecordRun", "8353643af2281da9b08b629d387d1d91ba0c8db2b2afbedc24cbfb3042854039"}, |
| // The box closes a run an agent restart interrupted through SetRecordRun (runFinishOurs). |
| - // Re-reviewed for run-lifecycle-logs-325 (amadan #325, proposed): reads result.json before and after its create-only write and emits the run's end as a member log event only when the result is its own; record writes unchanged. |
| - {"internal/host/runs.go", "runsWorker.closeInterrupted", "record-writer", "key:SetRecordRun", "fdfbca50d70569c82d6846734f32ec2d518126b334034f755418fe9fd06224bc"}, |
| + // Re-reviewed for run-lifecycle-logs-325 (amadan #325, proposed): reads result.json before and after its create-only write and emits the run's end as a member log event only when the result is its own and its done CAS landed (at most once); record writes unchanged. |
| + {"internal/host/runs.go", "runsWorker.closeInterrupted", "record-writer", "key:SetRecordRun", "b540e246a977470c50d3ebbbef080f9b8a824490fd3bb8bd6f98537bc882adee"}, |
| // The box finishes a run of this boot whose result is written through SetRecordRun (RunFinish). |
| {"internal/host/runs.go", "runsWorker.finishStranded", "record-writer", "key:SetRecordRun", "2d775d33b9ba97e4ed2953eb1880d33affcfba95fe5857dcdde72eb0f9acadf0"}, |
| // The box picks a queued run up by CAS through SetRecordRun (RunPick). |
diff --git a/internal/provision/testdata/plan14-root-reads-run-lifecycle-logs-325.tsv b/internal/provision/testdata/plan14-root-reads-run-lifecycle-logs-325.tsv| index 371dd33f4..29d2dce11 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-run-lifecycle-logs-325.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-run-lifecycle-logs-325.tsv |
| @@ -1,6 +1,6 @@ |
| # Census overlay run-lifecycle-logs-325 (amadan #325: the box's carlos run worker emits run started and run ended as info-level member log events through the agent's EventSink), loaded LAST, after bootstrap-carlos-follows-316. Proposed; ruling asked of the census owner on #280. |
| # No root read, file read or record write is added: the events are pure string formatting plus one EventSink.Event call, so runAgent (which passes the sink to armRunsPass) and runsWorker.execute (which emits the two events) keep their class, task and read sites, and only their digests, runAgent's Route offset and the internal/host envelope move. execute is accepted-legacy, so its declaration is re-pinned and the seal moves. |
| -@coverage internal/host fd22df93af01f39dae392f172bbc5458aeb1728017925f148240aac518c14931 |
| +@coverage internal/host 2866a11e1992a250b09dd560c7186c4fa42ad1019c6415dc03d82ccaca9dd832 |
| @legacy 6f7ae8c101088f20464d1a727c898190b04726de1f08ee6040212be2ad840caa internal/host/runexec.go runsWorker.execute legacy accepted-legacy accepted-legacy;rev3=census-ruling-carlos-run;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): runs one picked run to its result in the bucket; every root action goes through instance control;rev3=PROPOSED-325;identity=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos);basis=emits the run's start and end as member log events through the agent's EventSink; reads unchanged file:Prepare@1838,whole:Route@2539 1b075de70048003120c62153a50f6d83bd3eff6615f9ea5008ddc747846c8fbb |
| internal/host/runexec.go runsWorker.execute legacy accepted-legacy accepted-legacy;rev3=census-ruling-carlos-run;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): runs one picked run to its result in the bucket; every root action goes through instance control;rev3=PROPOSED-325;identity=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos);basis=emits the run's start and end as member log events through the agent's EventSink; reads unchanged file:Prepare@1838,whole:Route@2539 1b075de70048003120c62153a50f6d83bd3eff6615f9ea5008ddc747846c8fbb |
| internal/host/agent.go runAgent authority B,C,D,F rev3=census-ruling-seeded-instances;basis=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos): main's body with wireSeedPlacement called in the activator start's Configure step, before armMovesPass, and the startup sweep's result stored for the import rollback in Recover; read sites unchanged, offsets moved; main's B,C,D,F authority retained PROPOSED E46: captured claim selection and root journal mutation; root RPC outside service host exclusion; SQL/route data never grants root authority. Executing identity: service controller; root for inventory/retirement callbacks.;rev3=PROPOSED-325;identity=the agent (/opt/carlos/carlos agent, carlos-edge.service, user carlos);basis=passes the agent's log sink to armRunsPass; reads unchanged file:Exec@13802,file:Open@731,registry:App@10065,whole:Route@10057,whole:Route@23122 9250dbd16a071a1f01e835f0fb8419e4de59effaa6f1d178b23547957ff66d86 |