carlos / platform Public

census-astra-approvals

Squashed from census-astra-approvals into main.
claude agent, operated by paul@keymail.dev c83ab21072bc307d51a8a7e426e0a3a4724b19f7 parent 8c899da
3 files changed, +66 −15
  • AGENTS.md +33 −9
  • internal/provision/census_propose_internal_test.go +12 −1
  • internal/provision/census_propose_test.go +21 −5

CI passed — run details

CI log
=== gate ===
.amadan/test-gate.sh
=== launchctx (1 s)
ok  	amadan.net/carlos/platform.git/launchctx	0.013s
?   	amadan.net/carlos/platform.git/launchctx/controlname	[no test files]
?   	amadan.net/carlos/platform.git/launchctx/wireid	[no test files]
=== vet (20 s)
=== gofmt (1 s)
gofmt -l .: clean
=== rest (72 s)
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	4.138s
ok  	github.com/carlosframework/platform/cmd/moveprobe	11.869s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.817s
ok  	github.com/carlosframework/platform/internal/activator	58.229s
ok  	github.com/carlosframework/platform/internal/agentsock	0.021s
ok  	github.com/carlosframework/platform/internal/alerting	0.007s
ok  	github.com/carlosframework/platform/internal/analytics	0.022s
ok  	github.com/carlosframework/platform/internal/appconfig	2.943s
ok  	github.com/carlosframework/platform/internal/apperr	0.159s
ok  	github.com/carlosframework/platform/internal/applog	0.209s
ok  	github.com/carlosframework/platform/internal/assetcache	0.145s
ok  	github.com/carlosframework/platform/internal/awsinv	0.007s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.010s
ok  	github.com/carlosframework/platform/internal/boxid	1.103s
?   	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.011s
ok  	github.com/carlosframework/platform/internal/caps	0.019s
ok  	github.com/carlosframework/platform/internal/certwarm	0.004s
ok  	github.com/carlosframework/platform/internal/cgroup	0.845s
ok  	github.com/carlosframework/platform/internal/changelog	0.536s
ok  	github.com/carlosframework/platform/internal/claimactivation	1.441s
ok  	github.com/carlosframework/platform/internal/clamd	0.060s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
?   	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	7.714s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.013s
ok  	github.com/carlosframework/platform/internal/dbquery	0.008s
ok  	github.com/carlosframework/platform/internal/dbreads	2.191s
?   	github.com/carlosframework/platform/internal/dbreads/dbreadstest	[no test files]
ok  	github.com/carlosframework/platform/internal/dbverify	0.006s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.008s
ok  	github.com/carlosframework/platform/internal/docsite	0.011s
ok  	github.com/carlosframework/platform/internal/domains	3.416s
ok  	github.com/carlosframework/platform/internal/doorkey	0.014s
ok  	github.com/carlosframework/platform/internal/economics	0.005s
ok  	github.com/carlosframework/platform/internal/edge	45.316s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.015s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.186s
ok  	github.com/carlosframework/platform/internal/gate	1.658s
ok  	github.com/carlosframework/platform/internal/genselect	0.023s
ok  	github.com/carlosframework/platform/internal/geo	0.015s
ok  	github.com/carlosframework/platform/internal/geoip	0.044s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.235s
ok  	github.com/carlosframework/platform/internal/hostactors	6.470s
ok  	github.com/carlosframework/platform/internal/hostactors/awsmembership	0.015s
ok  	github.com/carlosframework/platform/internal/hostbox	2.992s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.700s
ok  	github.com/carlosframework/platform/internal/hostenv	0.015s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.015s
ok  	github.com/carlosframework/platform/internal/hostexec	5.322s
ok  	github.com/carlosframework/platform/internal/hostfence	0.037s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.623s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.020s
ok  	github.com/carlosframework/platform/internal/hostguard	1.100s
ok  	github.com/carlosframework/platform/internal/hostops	1.838s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.015s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.017s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.546s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.202s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.004s
ok  	github.com/carlosframework/platform/internal/infraview	0.004s
ok  	github.com/carlosframework/platform/internal/instancectl	5.236s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.785s
ok  	github.com/carlosframework/platform/internal/instancefs	0.008s
ok  	github.com/carlosframework/platform/internal/instancekey	0.008s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.008s
ok  	github.com/carlosframework/platform/internal/layout	1.678s
ok  	github.com/carlosframework/platform/internal/lbhealth	0.006s
ok  	github.com/carlosframework/platform/internal/lease	10.160s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.494s
ok  	github.com/carlosframework/platform/internal/ledger	0.019s
ok  	github.com/carlosframework/platform/internal/mail	0.214s
ok  	github.com/carlosframework/platform/internal/maintenance	0.128s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.569s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.107s
ok  	github.com/carlosframework/platform/internal/metrics	0.007s
ok  	github.com/carlosframework/platform/internal/moves	24.020s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.888s
ok  	github.com/carlosframework/platform/internal/objstore	0.237s
ok  	github.com/carlosframework/platform/internal/ops	0.189s
ok  	github.com/carlosframework/platform/internal/park	0.228s
ok  	github.com/carlosframework/platform/internal/plans	0.012s
ok  	github.com/carlosframework/platform/internal/privatefile	0.005s
ok  	github.com/carlosframework/platform/internal/provision	26.344s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.011s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	5.611s
ok  	github.com/carlosframework/platform/internal/relay	7.238s
ok  	github.com/carlosframework/platform/internal/release	26.566s
ok  	github.com/carlosframework/platform/internal/rollout	3.859s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.006s
ok  	github.com/carlosframework/platform/internal/rootaws	0.007s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.060s
ok  	github.com/carlosframework/platform/internal/rootstate	0.026s
ok  	github.com/carlosframework/platform/internal/runs	0.581s
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.673s
ok  	github.com/carlosframework/platform/internal/seeds	0.011s
ok  	github.com/carlosframework/platform/internal/sim/engines	2.188s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	10.946s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	3.609s
ok  	github.com/carlosframework/platform/internal/status	0.085s
ok  	github.com/carlosframework/platform/internal/steering	0.007s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.143s
ok  	github.com/carlosframework/platform/internal/svcrole	0.003s
ok  	github.com/carlosframework/platform/internal/telemetry	0.005s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.005s
ok  	github.com/carlosframework/platform/internal/unixdial	0.004s
ok  	github.com/carlosframework/platform/internal/updatewords	0.005s
ok  	github.com/carlosframework/platform/internal/usage	0.009s
ok  	github.com/carlosframework/platform/internal/version	0.004s
ok  	github.com/carlosframework/platform/internal/vet	1.608s
=== internal_console@0 (73 s)
ok  	github.com/carlosframework/platform/internal/console	70.594s
=== internal_console@1 (47 s)
ok  	github.com/carlosframework/platform/internal/console	45.539s
=== internal_console@2 (43 s)
ok  	github.com/carlosframework/platform/internal/console	41.362s
=== internal_console@3 (47 s)
ok  	github.com/carlosframework/platform/internal/console	45.407s
=== internal_console@4 (44 s)
ok  	github.com/carlosframework/platform/internal/console	42.077s
=== internal_console@5 (49 s)
ok  	github.com/carlosframework/platform/internal/console	46.667s
=== internal_console@6 (47 s)
ok  	github.com/carlosframework/platform/internal/console	45.376s
=== internal_console@7 (50 s)
ok  	github.com/carlosframework/platform/internal/console	48.482s
=== internal_host@0 (66 s)
ok  	github.com/carlosframework/platform/internal/host	63.077s
=== internal_host@1 (33 s)
ok  	github.com/carlosframework/platform/internal/host	30.595s
=== internal_host@2 (32 s)
ok  	github.com/carlosframework/platform/internal/host	29.493s
=== internal_host@3 (31 s)
ok  	github.com/carlosframework/platform/internal/host	29.443s
=== root@0 (71 s)
ok  	github.com/carlosframework/platform	68.996s
=== root@1 (49 s)
ok  	github.com/carlosframework/platform	47.283s
=== root@2 (61 s)
ok  	github.com/carlosframework/platform	59.109s
=== root@3 (53 s)
ok  	github.com/carlosframework/platform	50.821s
=== root@4 (59 s)
ok  	github.com/carlosframework/platform	56.828s
=== 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: 50659490 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: cached, digest verified
  mc: cached, 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.02s)
    --- 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.01s)
        --- 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.00s)
        --- 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.024s
=== RUN   TestT4StorageProbeMinio
2026/10/25 08:56:42 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E1B9F829ABA685, 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.022s
=== RUN   TestS3StoreMinioContract
=== RUN   TestS3StoreMinioContract/conditional_get_and_etag_listing
=== RUN   TestS3StoreMinioContract/bump_through_every_mutation_path
--- PASS: TestS3StoreMinioContract (0.03s)
    --- PASS: TestS3StoreMinioContract/conditional_get_and_etag_listing (0.01s)
    --- PASS: TestS3StoreMinioContract/bump_through_every_mutation_path (0.02s)
PASS
ok  	github.com/carlosframework/platform/internal/release	0.031s
Bucket created successfully `local/dbf-deploy`.
Bucket created successfully `local/dbf-data-ie`.
Bucket created successfully `local/dbf-data-uk`.
local/dbf-deploy versioning is enabled
Created policy `dbf-console` successfully.
Created policy `dbf-ukbox` successfully.
Added user `dbfconsole` successfully.
Added user `dbfukbox` successfully.
Attached Policies: [dbf-console]
To User: dbfconsole
Attached Policies: [dbf-ukbox]
To User: dbfukbox
=== RUN   TestDBForwardMinio
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/25 08:56:44 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/25 08:56:44 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=370
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000003
2026/10/25 08:56:44 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000003 maxTXID=0000000000000003 size=454
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000003 txid.db=0000000000000003
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/25 08:56:44 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/25 08:56:44 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/25 08:56:44 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/25 08:56:44 console: could not notify member@example.com of a CLI token issue: keymail: lookup member@example.com: keymail: GET /api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A46181: could not determine target mailbox
    agent.go:135: dbreads: dbr_ae2jyboqis6m22p7tlv7noifvy: planned 1 files (720 bytes) and restored to 1 in 8ms
    agent.go:135: dbreads: dbr_ae2jyboqis6m22p7tlv7noifvy: output 5 bytes in 1 confirmed chunks in 1ms
2026/10/25 08:56:45 console: db read dbr_ae2jyboqis6m22p7tlv7noifvy: relayed 1 chunks (102 bytes) in 1.004s, ended done
    agent.go:135: dbreads: dbr_ih6dfwzww7lqhw5bnv4uppbvre: planned 3 files (1544 bytes) and restored to 3 in 9ms
    agent.go:135: dbreads: dbr_ih6dfwzww7lqhw5bnv4uppbvre: output 5 bytes in 1 confirmed chunks in 1ms
2026/10/25 08:56:46 console: db read dbr_ih6dfwzww7lqhw5bnv4uppbvre: relayed 1 chunks (102 bytes) in 1.004s, ended done
    agent.go:135: dbreads: dbr_rqmig7hprj47d3ustql46l4xce: planned 3 files (1544 bytes) and held through 3 in 12ms
    agent.go:135: dbreads: dbr_rqmig7hprj47d3ustql46l4xce: output 1544 bytes in 1 confirmed chunks in 1ms
2026/10/25 08:56:47 console: db read dbr_rqmig7hprj47d3ustql46l4xce: relayed 1 chunks (1641 bytes) in 1.005s, ended done
=== RUN   TestDBForwardMinio/by_disconnect
=== NAME  TestDBForwardMinio
    agent.go:135: dbreads: dbr_nkytsyozd4mfkltmfixjtic5la: planned 3 files (1544 bytes) and restored to 3 in 12ms
2026/10/25 08:56:47 console: db read dbr_nkytsyozd4mfkltmfixjtic5la: relayed 0 chunks (0 bytes) in 32ms, ended client gone
    agent.go:135: dbreads: dbr_nkytsyozd4mfkltmfixjtic5la: output 5 bytes in 1 confirmed chunks in 105ms
=== RUN   TestDBForwardMinio/by_timeout
=== NAME  TestDBForwardMinio
    agent.go:135: dbreads: dbr_os2ufszxtdhdtjyuhawawny73m: planned 3 files (1544 bytes) and restored to 3 in 12ms
2026/10/25 08:57:07 console: db read dbr_os2ufszxtdhdtjyuhawawny73m: relayed 0 chunks (0 bytes) in 19.042s, ended no-result
    agent.go:135: dbreads: dbr_os2ufszxtdhdtjyuhawawny73m: output 5 bytes in 1 confirmed chunks in 19.014s
--- PASS: TestDBForwardMinio (22.50s)
    --- PASS: TestDBForwardMinio/by_disconnect (0.24s)
    --- PASS: TestDBForwardMinio/by_timeout (19.15s)
PASS
ok  	github.com/carlosframework/platform/internal/console	22.517s
minio contract: PASS
diff --git a/AGENTS.md b/AGENTS.md
index 59be1f922..a57f05a58 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -222,13 +222,15 @@ anything.
GOPROXY=off`; go commands may need the sandbox disabled ("read-only file
system" from the toolchain is the tell).
-## The census: an approval, not a decision (Paul, 2026-10-24, #369)
+## The census: an approval, not a decision (Paul, 2026-10-24, #369; reviewer, 2026-10-25)
`TestPlan14RootReadCensus` (`internal/provision`) fails when a branch
changes a unit that reads root-owned state, or a package inside the
-census envelope. Every such change needs the census owner's approval on
-#280 before the merge. Most are approvals: the same reads in an edited
-body, or an envelope fingerprint that moved. Get them approved like this:
+census envelope. Every such change needs an approval on #280 before the
+merge. Most are approvals: the same reads in an edited body, or an
+envelope fingerprint that moved. An independent reviewer gives those;
+the census owner (Paul) sees only decisions and escalations. Get them
+approved like this:
1. `make census-card` prints what changed and writes nothing. Each change
has a class:
@@ -251,18 +253,40 @@ body, or an envelope fingerprint that moved. Get them approved like this:
if any step fails. If the code changes again, rerun it with the same
NAME: it replaces its own overlay (only one it wrote itself), which must
still be the last line of the chain.
-4. Show the card to the census owner and ask for an approval. Ask in the
- session when they are there (one question: approve or hold); otherwise
- post the card on #280. Record the approval on #280: who, when, and the
- tip. Rows carry `rev3=census-ruling-<NAME>` from the start, so there is
+4. Get the approval from an independent reviewer: a model from the other
+ family than the one that wrote the change (Astra for a Claude author,
+ Opus or Fable for a Codex one), never the author itself. Give it the
+ card and the branch's diff against main on the amadan.net remote
+ (`origin` or `amadan`, as in the steps above). It gives a verdict for
+ every unit and every re-pinned envelope, citing the lines it read:
+ - a unit is compared with its recorded disposition and basis, which
+ the card lists;
+ - an envelope means every changed function in that package and what
+ it reaches, since an envelope re-pin is how the census sees reads its
+ scanner cannot attribute to a unit.
+
+ It escalates if the change lets root open, run, query or follow
+ anything it did not before from a path or input a tenant, an app or a
+ box agent can create, write or name; if it weakens a check, refusal,
+ validation, argument or environment the basis relies on; or if the
+ diff cannot settle either. Otherwise it approves. Record each verdict
+ on #280 with the reviewer and the tip. An escalation goes to the
+ census owner as a decision.
+ Rows carry `rev3=census-ruling-<NAME>` from the start, so there is
nothing to flip afterwards and no second CI run.
+Why a reviewer and not an automatic pass: the classes count kinds of
+read, not where a read points, so a `body` change can aim an existing
+`Open` at a tenant-writable path. Someone other than the author has to
+read the diff, but it need not be the owner.
+
The tool refuses decisions (`reads`, `new`, `removed`, `other`): a new
kind of read can make a unit's disposition wrong, so it is the owner's
call, not an approval. Write those rows by hand, following the overlays in the
chain, and ask on #280 with the card attached; the census owner decides
those. Nothing about the census's strength changes: every changed read is
-still named, digested and seen by a person before merge. The record-write
+still named, digested and read by someone other than its author before
+merge. The record-write
census (9B8, the `*_census_test.go` inventories) still takes hand-written
entries.
diff --git a/internal/provision/census_propose_internal_test.go b/internal/provision/census_propose_internal_test.go
index 19a267956..782e197fa 100644
--- a/internal/provision/census_propose_internal_test.go
+++ b/internal/provision/census_propose_internal_test.go
@@ -168,7 +168,7 @@ func TestCensusProposalClosesEveryApprovableChange(t *testing.T) {
t.Errorf("row does not name the ruling: %s", line)
}
}
- for _, want := range []string{"census-ruling-wake-timing", "internal/a/a.go", "body", "sites", "+registry:Host", "internal/a", seal, p.Seal, "logs the wake's timing"} {
+ for _, want := range []string{"census-ruling-wake-timing", "internal/a/a.go", "body", "sites", "+registry:Host", "internal/a", seal, p.Seal, "logs the wake's timing", censusReviewQuestion, "Recorded dispositions"} {
if !strings.Contains(p.Card, want) {
t.Errorf("card lacks %q:\n%s", want, p.Card)
}
@@ -371,3 +371,14 @@ func TestCensusClassifiesAKeyWithASpace(t *testing.T) {
t.Fatalf("changes = %+v, want one body for %q", cs, key)
}
}
+
+// An envelope-only card still asks the reviewer for a verdict: a re-pin
+// is how the census sees reads it cannot attribute to a unit (Astra
+// review, census-astra-approvals P1).
+func TestCensusCardReviewsAnEnvelopeAlone(t *testing.T) {
+ cs := []censusChange{{Key: "internal/a", Class: censusEnvelope, OldHash: strings.Repeat("1", 64), NewHash: strings.Repeat("2", 64)}}
+ card := rootReadCard("x", cs, nil, strings.Repeat("3", 64), "")
+ if !strings.Contains(card, censusReviewQuestion) || !strings.Contains(card, "`internal/a`") {
+ t.Errorf("card:\n%s", card)
+ }
+}
diff --git a/internal/provision/census_propose_test.go b/internal/provision/census_propose_test.go
index 7dd3fa823..415fa5501 100644
--- a/internal/provision/census_propose_test.go
+++ b/internal/provision/census_propose_test.go
@@ -6,8 +6,8 @@ package provision
// the same reads in an edited body, or an envelope fingerprint that moved.
// This file classifies each discrepancy, writes the overlay that closes the
// approvable ones (rows, @legacy fingerprints, envelope re-pins, the seal,
-// the chain manifest line), and the approval card a person reads to
-// approve it. Changes that need a disposition (a new unit, a removed one,
+// the chain manifest line), and the approval card an independent reviewer
+// reads to approve it. Changes that need a disposition (a new unit, a removed one,
// the scanner's field list) are decisions: it names them and writes
// nothing.
//
@@ -291,7 +291,7 @@ func proposeRootReads(got rootReadScan, inv rootReadInventory, seal, name string
plain = append(plain, row)
}
header := []string{
- fmt.Sprintf("# Census overlay %s, "+censusProposeMark+". %s, approved by the census owner before the merge; the approval card is docs/superpowers/notes/census-proposal-%s.md.", name, ruling, name),
+ fmt.Sprintf("# Census overlay %s, "+censusProposeMark+". %s, approved on #280 before the merge; the approval card is docs/superpowers/notes/census-proposal-%s.md.", name, ruling, name),
fmt.Sprintf("# %d body, %d sites, %d envelope: each changed unit re-frozen at its existing disposition with its claim appended.", counts[censusBody], counts[censusSites], counts[censusEnvelope]),
}
overlay := strings.Join(append(append(append(header, frozen...), plain...), cover...), "\n") + "\n"
@@ -324,8 +324,10 @@ func censusDiscrepancies(got rootReadScan, inv rootReadInventory, seal string) [
return append(errs, checkRootReadDeclarations(got, inv)...)
}
-// rootReadCard is what a person reads to approve the change. newSeal is
-// empty for a dry run (make census-card).
+// rootReadCard is what the reviewer reads to approve the change (AGENTS.md,
+// The census): someone other than the author, because the classes count
+// kinds of read, not where a read points. newSeal is empty for a dry run
+// (make census-card).
func rootReadCard(name string, cs []censusChange, claims map[string]string, seal, newSeal string) string {
var b strings.Builder
fmt.Fprintf(&b, "# Census approval: %s\n\n", name)
@@ -365,6 +367,16 @@ func rootReadCard(name string, cs []censusChange, claims map[string]string, seal
if len(envelopes) > 0 {
fmt.Fprintf(&b, "Envelopes re-pinned: %s.\n\n", strings.Join(envelopes, ", "))
}
+ if len(units) > 0 || len(envelopes) > 0 {
+ b.WriteString(censusReviewQuestion)
+ }
+ if len(units) > 0 {
+ b.WriteString("Recorded dispositions, which each change must still satisfy:\n\n")
+ for _, c := range units {
+ fmt.Fprintf(&b, "- `%s` `%s`: %s, task %s. Basis: %s\n", c.Before.File, c.Before.Unit, c.Before.Class, c.Before.Task, c.Before.Basis)
+ }
+ b.WriteString("\n")
+ }
if len(decisions) > 0 {
b.WriteString("Decisions (write their rows by hand and ask on #280):\n\n")
for _, c := range decisions {
@@ -387,6 +399,10 @@ func rootReadCard(name string, cs []censusChange, claims map[string]string, seal
return b.String()
}
+// censusReviewQuestion is what the independent reviewer answers for the
+// approvals on a card (AGENTS.md, The census).
+const censusReviewQuestion = "Review: a model from the other family than the author reads the branch's diff against main on the amadan.net remote (`origin` or `amadan`, whichever points at amadan.net) and gives a verdict for every unit and every re-pinned envelope above. For a unit, compare the change with its recorded disposition and basis below; for an envelope, read every changed function in the package and what it reaches. Escalate if the change lets root open, run, query or follow anything it did not before from a path or input a tenant, an app or a box agent can create, write or name; if it weakens a check, refusal, validation, argument or environment the basis relies on; or if the diff cannot settle either. Otherwise approve. Record each verdict on #280 with the tip. An escalation goes to the census owner as a decision.\n\n"
+
// censusProposeMark is in the first line of every overlay census-propose
// writes: a re-run replaces only a file that carries it.
const censusProposeMark = "written by census-propose (#369)"