Repository navigation
feat(cli): collapse the surface to site and bench - #7
Merged
Merged
Conversation
semibase is a setup script: one run brings an instance to a known state and exits. The old surface named the tool's internal steps (config, create, verify) plus a mechanism (all, whose only caller was this repository's CI). It is now two commands named for the two situations, and they differ in exactly one thing: site applies the ALTER SYSTEM memory constants, bench does not. config, create, verify and all are removed with no alias and no deprecation path. public.trends moves into this tool, in both modes. It was created by Simple-Scada 2 on a site and by SemiPlot's seeder on a bench, so the reader's access to it was unknowable until after the SCADA's first start. sql/trends.sql carries the vendor's shape verbatim - partitioned by range on t, primary key tpk, the tpdefault catch-all - embedded like sql/semiplot_tags.sql and applied over a scada_writer login of its own, never SET ROLE, so the reader's SELECT arrives through the default privileges set for that role. messages is not created and day partitions are not created; both stay with their writer. verify's two load-bearing checks become the tail of both commands, where they are now knowable: the reader holds SELECT on public.trends and does not hold INSERT, plus the pending-restart warning on site. The messages check, the "writer has not run yet" inference and the asPartOfAll branch go with the commands they served. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tail check asked has_table_privilege, which reads one catalog bit. After REVOKE USAGE ON SCHEMA public FROM public it answers t while every read the reader issues fails with "permission denied for schema public" - measured on postgres:17-alpine. The read itself is the check now: SET ROLE semiplot_reader, then SELECT count(*) FROM (SELECT 1 FROM public.trends LIMIT 1) probe. The catalog bit is asked only after that read has failed, to split a missing table grant from a schema the reader cannot enter; each answer prints its own repair, and the grant repair again names both statements and the re-run. Over TCP, with a reader password in the run, the reader also logs in and reads - the half SET ROLE cannot reach, being pg_hba admission and the password a consumer carries. It is skipped with a note on a socket host and without that password. public.trends is created under SET ROLE scada_writer instead of over a scada_writer login. The login made the run depend on pg_hba admitting that role: on a socket with "local all all peer", the default on Debian, Ubuntu and RHEL, the superuser phases succeeded and the run then died with SQLSTATE 28000, leaving the database, both roles, the grants and semiplot_tags behind. Both routes were measured to leave the same relowner and the same relacl, so nothing about the reader's access path changes. Creating the table therefore needs no writer password; that password is now needed only on a first run, to create the role, and the usage text says so. The re-run path no longer says "exists, left untouched" and stops there: it requires public.tpdefault, since without it a row no day partition covers is rejected, and reports the tpdefault row count, which on a months-old table is the only signal that a day partition was missing at write time. The pending-restart read moves to the last statement of the tail, where the docs already claimed it was, and both commands run it - bench tunes nothing, but it runs against servers a site run tuned and nobody restarted. The DDL test pins the whole statement text of sql/trends.sql, columns, types and defaults included, and its messages guard now matches the unqualified name. The provisioning-order step and the Russian commissioning runbook hedge the SCADA-meets-an-existing-trends assumption the way provisioning.md does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
semibaseis a setup script: it brings an instance to a known state and exits, run once at commissioning and never again. The surface was named for its internal steps —config,create,verify,all— andallwas named for its mechanism, with exactly one caller on Earth: this repository's own CI.Two commands
They differ in one thing. That is only true because
public.trendsmoved into this tool and is now created in both modes — previously Simple-Scada made it on a site and SemiPlot's seeder made it on a bench, which was the second difference and the reason a bench tested a shape a site never had.verifyis gone as a command. It existed as a separate verb because its checks had to wait for the SCADA to create the table; once the tool creates it, they are knowable at exit and became the tail of both commands. Nobody was ever going to return and run it by hand.No aliases, no deprecation: the tool is in development and its only consumer is ours.
Two review findings worth reading, both measured
The privilege check did not check what it claimed.
has_table_privilegereads a catalog bit. AfterREVOKE USAGE ON SCHEMA public FROM publicthe tool reported "semiplot_reader holds SELECT" and exited 0, while an actualSELECTfailed withpermission denied for schema public. The load-bearing check is now a real read; the catalog question survives only to split "no table grant" from "cannot enter the schema", each with its own repair.Creating the table over a
scada_writerlogin added an undocumented pg_hba requirement. On a socket withlocal all all peer— the default on Debian, Ubuntu and RHEL — the login failed with peer authentication after the database, both roles, the grants andsemiplot_tagswere already created. Reproduced against the pre-fix binary, then fixed bySET ROLEon the superuser connection. Measured A/B: identical owner and identical ACL,semiplot_reader=r/scada_writer. The fidelity argument for a real login was about archiving, not about creating a table.A run carrying only
SEMIBASE_SUPER_PASSWORDnow completes end to end over a socket.Verified against containers, not argued
benchandsiteon a freshpostgres:17-alpine, twice each for idempotency; the revoked-schema case now fails with the repair named; atrendsmissingtpdefaultis caught rather than reported as "left untouched" (it accepts no inserts); a non-emptytpdefaultwarns that a day partition was missing at write time;host all semiplot_reader all rejectproves the live reader login is load-bearing when it runs; and the init-script path completes during initdb over the socket.One thing recorded rather than solved
Whether Simple-Scada tolerates finding
trendsalready present is unmeasured. The expectation is that it writes into the existing table the way it would after a reconnect, and the shape it finds is the shape it makes — but nobody has watched it.docs/architecture/provisioning.mdcarries it as an explicitly unverified assumption with the experiment that settles it:log_statement = 'all', one SCADA start against asemibase sitedatabase, read the DDL from the log.The locked-decisions table is amended in the same PR: the vendor still owns the schema's evolution; this tool creates the table once with the vendor's shape and never alters it.
🤖 Generated with Claude Code