Repository navigation
Testing and Release Validation
The public tree provides compiler-gated builds and binary version smoke checks.
It does not currently ship a complete automated end-to-end harness. Release
confidence therefore requires both make test and an isolated integration
exercise of the features being changed.
Check the toolchain and build optimized binaries:
./configure
make check
make testmake test performs a release build of pizarra, tiza, and pzweb, then
runs each binary's --version path. Compiler warnings are treated as errors.
This catches compilation, linking, and version-entry-point failures; it does not
open sockets or mutate a store.
Build with runtime checks and debug information before investigating memory, range, overflow, or concurrency failures:
make debug
./pizarra-debug --version
./tiza-debug --version
./pzweb-debug --versionCompiled units are isolated under build/release, build/debug, and
build/tiny; changing targets cannot silently reuse units produced with a
different compiler flag set.
The public CI checkout and test path use shell tooling, Free Pascal, SQLite, and tmux. They do not install or invoke Node.js, npm, or a JavaScript package runner; browser assets are shipped directly and must keep that property.
make static intentionally fails because the threaded programs use the system
C runtime and load SQLite dynamically. Do not treat that failure as a test
regression.
The repository includes focused regression harnesses. Each creates only a
disposable temporary tree, below /tmp in the default environment:
scripts/test-bootstrap-layout.sh
scripts/test-private-config.sh
scripts/test-group-onblock.sh
scripts/test-pzshare-symlink.sh
scripts/test-migrate-release-layout.sh
scripts/test-retire-repo-runtime-conf.sh
scripts/test-install-service.sh
scripts/test-pzweb-readiness.sh
scripts/test-session-preservation.sh
scripts/test-watchdog-session.shThe bootstrap harness compiles the real Pascal layout implementation and checks
mode 0711 configuration, mode 0700 state/release/log directories, matching
mode-0600 hub/client credentials, the generated canonical release path,
non-overwriting repair of a missing release directory, rejection of unsafe
release permissions, lock cleanup, and coherent systemd StateDirectory
ownership. The private-config harness compiles a disposable Pascal client
against the real typed loaders. It checks quoted-value semantics, final and
ancestor symlink rejection, exact mode-0600 enforcement, rejection of a
non-sticky writable ancestor, and that a pathname replacement cannot redirect
an already opened descriptor. The shared-copy harness compiles the real
pzshare implementation, preplants a temporary-name symlink to a protected
file, and proves that the target and adversarial link remain untouched while
the payload is published as an exact mode-0644 regular file. The
release-layout harness verifies a
non-mutating dry run; exact preservation of entry names, types, empty
directories, and file bytes; mode 0700 on the canonical release root; an
idempotent canonical rerun and final lineage record;
rejection of conflicting non-empty trees before destination mutation; and the
mandatory explicit source for a relative legacy release path. The retirement
harness exercises both dry-run and confirmed repository-runtime retirement,
including canonical release-path validation. Run them whenever migration,
canonical paths, source archival, release publication, or retirement checks
change. None operates on /etc/pizarra, /var/lib/pizarra, the checkout's
.private tree, or live tmux sessions.
The installer harness stages only below a disposable DESTDIR. It verifies
that configured binary/data paths reach compiled defaults, rendered examples,
and systemd units; rejects a publication path different from the certified
build; checks idempotent reinstall; injects a post-payload failure and compares
the restored bytes and metadata; checks manifest-driven uninstall; and proves
that explicit first-cutover adoption can replace and record a pre-existing
payload inside the disposable root. The
web-readiness harness starts an isolated loopback hub and web console, proves
that health fails before bind and succeeds after a real TCP connection, then
proves a second server fails promptly on the occupied port without claiming it
is listening. The watchdog-session harness uses a private tmux socket and proves
that a configured launch is created and ownership-tagged while an absent manual
session with no launch is neither fabricated nor falsely logged as respawning.
The session-preservation harness is stricter and entirely hermetic: a fake
tmux executable proves that pizarra --host-session creates an absent hub
session exactly once, performs only an existence probe on the second call, and
has no destructive command path. It never connects to any tmux server.
Use a temporary directory, loopback-only listeners, new disposable secrets, and ports that do not overlap a live installation. Do not point tests at a production config or store.
The smallest useful topology contains:
- one hub;
- a console identity using the master credential;
- two inbox-only teams with distinct bound credentials;
- one bound web identity with all required delegated families;
- the bundled web assets; and
- a temporary shared directory when file operations are under test.
Use exact 0600 mode for the web config. Keep all listeners on 127.0.0.1.
Stop the isolated hub and remove only the known temporary directory when the
test is complete.
- An empty, wrong, or unknown credential is rejected.
- A team credential cannot claim another identity.
- A team credential cannot call an undelegated family.
- A deliberately delegated family succeeds without granting unrelated access.
- Team credentials are unique and differ from the master credential.
- With
master_console_only = on, the master credential cannot act as a team.
- A direct message receives a monotonic sequence.
- An offline destination returns queued state and receives the message later.
- Inbox peek does not advance the cursor.
- Paged
afterreads do not consume messages, includingafter=0. -
ackadvances only to an eligible sequence and rejects a value above it. - A group broadcast creates one durable entry per selected destination and reports partial delivery accurately.
- Restarting the hub preserves journal order and cursor state.
- Multiline text arrives as one bracketed paste followed by one Enter.
- A lost delivery acknowledgement is retried.
- A persisted daemon receipt acknowledges a duplicate sequence without a second terminal paste.
- An unwritable receipt-state path is visible and does not pretend to provide restart-safe deduplication.
- A declared missing session is recreated, while removing its configuration does not kill an existing session.
Test remote push and reverse dial separately. Reverse dial must reject a claimed team outside the bound credential's configured service scope and must refuse a second active channel for the same team.
- Replay starts strictly after the requested cursor.
- A normal team sees only traffic to or from itself.
- Delegated watch observes the intended wider scope.
- Keepalives do not alter durable message state.
- Queue/replay overflow, unavailable history, and a cursor ahead of the journal
produce explicit
gapevents and require reload.
- Parent/child task integrity and terminal-state transitions are enforced.
- A workflow rejects missing dependencies and cycles.
- Parallel siblings activate together and the next topological level waits for all lower-level siblings.
- Activation creates one linked task; reconciliation repairs a missing projection without duplicating it.
- Strict mode rejects completion without evidence.
- A console-owned approval gate cannot be closed by a non-console identity.
- An error halts every branch and preserves the previously active set.
- The fixer cannot verify its own repair; failed verification stays halted; successful independent verification resumes the correct state.
- Cross-workflow completion activates eligible dependent work.
- Snapshot history, named undo, clone, save/restore, and exports preserve stable step IDs and valid dependencies.
- Abort cancels open linked tasks; deleting a workflow does not erase task history.
For the persisted group permission-block policy, build the debug binaries and run the focused disposable harness:
make debug
scripts/test-group-onblock.shThe harness creates its fixture only below /tmp. It covers console, group
boss, and ordinary-member authorization; alarm, log, and default input
validation; SQLite persistence across a hub restart; and the value returned by
group list.
An additional local browser regression runs the repository's real
web/apps/manage.js in Chromium without Node.js, npm, Playwright, or another
JavaScript package runner:
PIZARRA_TEST_CHROMIUM_BIN=chromium \
scripts/test-group-onblock-browser.shIt requires Python 3 and a Chromium-compatible executable, starts a strict
loopback fixture server, and keeps its browser profile and captured output
below /tmp. It checks the rendered when blocked: log only card, the exact
default, alarm, and log selector values, and the exact
POST /api/group/builders/onblock body {"onblock":"default"}. This test is
optional for local validation because Chromium is not a public-CI dependency.
- A missing implicit canonical installation creates matching mode-
0600hub and console credentials plus private state/log directories; an explicitly or environmentally selected missing config fails without fallback/bootstrap. - A second hub opening the same store fails on the persistent lifetime lock,
including the interval before the first hub starts listening; managed child
processes do not inherit that descriptor across
exec. - Legacy team/group/project/app input migrates into
org.sqlitewith exact counts, policies, manuals, and relations, then disappears from the active bootstrap INI only after verification and an owner-only backup. - A second
--migrate-onlyrun is idempotent: it preserves registry counts and does not recreate INI registry sections or another cutover marker. - Team parent cycles and missing references are rejected.
- Project and group administrators have only documented authority.
- Removing an object still referenced by open work or relations is refused.
- Application ownership, project roles, manuals, history, and undo survive a hub restart.
- When SQLite is unavailable, corrupt, or cannot be projected completely, hub startup fails visibly; no partial INI registry is served.
- Traversal, encoded separators, dot segments, NUL, and invalid basenames are rejected.
- Symlinks are not followed.
- Existing files are not overwritten; collisions produce a distinct name.
- Wrong offsets, malformed base64, excess sizes, and bad final SHA-256 digests fail without publishing a partial file.
- A verified upload/download round trip preserves exact bytes.
- Non-empty directory deletion requires explicit recursive intent and reports partial outcomes when the tree changes concurrently.
- Startup rejects permissive or symlinked configuration, wildcard listeners, invalid allowlists, incoherent host/origin, missing assets, unbound identity, console impersonation, and any missing delegated family.
- Every asset and API route requires admitted source, exact host, and Basic credentials.
- Mutations reject missing/wrong Origin, missing
X-Pizarra, wrong media type, unknown or duplicate keys, wrong types, query parameters, and bodies over 64 KiB. - CORS preflight is rejected and security headers are present on success and error responses.
- SSE resume, message direction, keepalive, gap, and terminal events match the documented contract.
- Browser operations distinguish
not_sent,unknown, andapplied_stale. - The Activity tab remains bounded while durable History still pages older messages.
- A backup is published only after all pieces and the manifest are complete.
- A configured external shared/NFS sentinel is absent from the backup payload;
only the
[shared] dirreference inside the included hub config remains. - Verification detects a missing, changed, duplicated, or unlisted piece, missing secret, invalid SQLite integrity/foreign keys, and undeclared SQLite WAL/shared-memory payload.
- Restore dry-run changes nothing and prints the exact target.
- Restore refuses a running/locked hub, a missing or unsafe persistent lock, unconfirmed execution, and an unaccepted credential mismatch.
- Restore moves previous state aside and does not retain stale SQLite sidecars.
- A privileged restore preserves the live configuration/store owners so a
dedicated-account hub can reopen every mode-
0600replacement. - The restored hub exposes the expected registries, tasks, workflows, cursors, and messages.
- Restore leaves external shared/NFS content unchanged; the restored hub
reference and pzweb
[web] sharedare rechecked for exact equality before service startup. - Layout migration preserves hub
[shared] dirand pzweb[web] sharedliterally, never traverses the mounted tree, and retirement leaves an external sentinel unchanged. - Release-layout migration preserves the selected tree exactly, leaves its
source untouched, rewrites
[server] releasesto/var/lib/pizarra/releases, and aborts before installation on conflicting non-empty trees. - A relative legacy release path is rejected unless the operator supplies its
reviewed absolute source with
--source-releases. - Repository-runtime retirement requires that exact canonical, real, private, outside-repository release directory and rejects symlinks and special entries.
- A release is not advertised until
VERSIONexists and matches the running hub. - Publication accepts only an absolute
RELEASEDIR, stages artifacts under temporary names, and renamesVERSIONlast. - Endpoint installation rejects a wrong digest, wrong version, oversized artifact, or incomplete download; a failed post-install self-test restores the previous executable, while verified success prunes rollback copies.
The highest-value failures occur between durable truth and its projection. Test at least these interruption points:
- after journal append but before endpoint acknowledgement;
- after workflow commit but before linked-task update or notification delivery;
- while several watchers consume replay and live traffic;
- during simultaneous inbox pagination and acknowledgement;
- during parallel uploads using separate transfer IDs;
- after staging an update but before atomic replacement; and
- after moving live files aside but before a restore finishes.
After each interruption, restart the affected process and verify that the system either completes reconciliation or reports a precise degraded/unknown outcome. A test must never infer rollback solely from a lost network response.
Before a public release:
- run
make testfrom a clean checkout; - exercise the relevant integration matrix on isolated state;
- check that all sample secrets and addresses are placeholders;
- scan tracked files for private paths, credentials, generated state, binaries, caches, and tool-specific development artifacts;
- verify every Markdown link and referenced screenshot exists;
- compare CLI help, configuration examples, API routes, and protocol docs with source;
- inspect the exact staged diff and
git ls-filesoutput; and - create release artifacts only from the committed tree.
Version choice is a release decision, not a documentation-only edit. Before
tagging, prove that src/pzver.pas, all three --version outputs, the
changelog heading, release notes, README, and every version-bearing wiki page
name the same release. Do not reuse the currently deployed version for changed
binaries, and do not publish a numerically older tag over a newer public
branch.
The project wiki is a separate Git repository from the source repository. Treat its commit and push as a distinct publication step: update it from the same source commit, inspect its own diff/status, and do not let a successful source push imply that the wiki was updated.
For a release <version>:
- freeze the source and version-bearing documentation;
- run the complete build and focused bootstrap, private-config, shared-copy, group-policy, release-layout migration, and retirement tests, plus the relevant isolated integration matrix;
- verify Markdown links, screenshots, tracked-file inventory, and both source and wiki diffs;
- commit and push the tested source commit;
- create an annotated
v<version>tag on that exact commit and push it; - create the GitHub Release from that tag with notes matching the changelog;
- commit and push the separately reviewed wiki update; and
- verify the public tag, release assets/notes, source pages, and wiki after publication.
make publish serves the fleet's tiza self-update artifacts and is not the
GitHub Release operation. It deliberately requires a clean committed tree;
both publication channels must refer to the same chosen suite version.
For the first GitHub publication, the fixed release tuple is suite 1.1.22,
annotated tag v1.1.22, and GitHub Release title Pizarra 1.1. This continues
the deployed 1.1 line after 1.1.21; the discarded, untagged development
placeholder is not a release and must never be presented as one.
When reporting a test result, include the suite version, operating system, architecture, Free Pascal version, SQLite availability, topology, exact command, expected result, actual result, and whether a retry was attempted.
Pizarra 1.1.41 · Source · Documentation · Issues · GPL-3.0-only · Sole author: Germán Luis Aracil Boned
pizarra Wiki
Start
Use
- Cooperation Patterns
- Organization Model
- Messaging and Inbox
- Tasks
- Workflow Engine
- Tiza CLI and Console
- Web Console
- Files and Transfers
Operate
- Configuration Reference
- Deployment Topologies
- Security Model
- Backup Restore and Updates
- Operations Runbook
Integrate
Project