usage.md (20621B)
SCP projects: archived build/run reference
SCP is an archived, separate language experiment. This guide records historical
usage; these steps are not tricu compiler acceptance or recovery prerequisites.
Sources, historical launcher/tests and original mixed fixtures are preserved in
archive/scp/. The active executable no longer has a
scp command, and the former SCP Nix targets no longer exist. Commands and paths
below describe historical checkouts, not the current checkout.
SCP compiles to reusable tree-calculus entries; configuration is ordinary SCP
computation returning named workers. See the language
contract for semantics. tricu scp owns typed
project parsing, bounded phase orchestration, and pinned execution in the
Haskell application. Compilation, configuration, and publication actions remain
in consumers.command.
Preparation loads full verified compiler/SCP artifacts, never compiler-library
source through Haskell. The checkout's .tri libraries and consumer runners are
native modules. Create an explicit bootstrap from the checked immutable fixtures:
mkdir -p .bootstrap-run/scp-bootstrap
cp test/fixtures/compiler-library.arboricx .bootstrap-run/scp-bootstrap/compiler-bootstrap.arboricx
cp test/fixtures/support-library.arboricx .bootstrap-run/scp-bootstrap/scp-bootstrap.arboricx
export SCP_BOOTSTRAP="$PWD/.bootstrap-run/scp-bootstrap"
These are retained artifacts, not automatic rebuilding of edits to library source.
--bootstrap selects this directory; absence/corruption fails, without recovery
fallback. Project workspace names select qualified artifact exports; explicit
workspace manifest pins select verified flat/indexed trees from the deployment
CAS. Compiler identity includes executable and full artifact bytes. Compilation,
configuration and publication still execute the tricu-owned command trees.
Normal project workflow
After nix build .#, prepare once and run the resulting deployment record:
result/bin/tricu scp prepare demos/consumers/prefix-lines/prefix-lines.scp-project \
--bootstrap "$SCP_BOOTSTRAP" --store /tmp/tricu-logs/cas --output /tmp/tricu-logs/logs.prepared --seconds 240
result/bin/tricu scp run /tmp/tricu-logs/logs.prepared \
--input-file demos/consumers/prefix-lines/tagged-log.fixture --seconds 60
# matches=3, first=disk
Use data-lines.scp-project for the unchanged-line filter. Declarations select
workspace/directory, configuration entry/input/state and a presentation profile
once. Paths are relative to the declaration; input/state explicitly choose
leaf:true, UTF-8 text, or raw-byte file. Change ERROR to WARN and
prepare another record to select warning payloads. The declaration selects
entries explicitly; input and state remain data.
For a direct tree program, select scp-invocation.result.v1 and an invocation
entry in presentation (no label or file adapter). For example:
result/bin/tricu scp prepare demos/consumers/fizzbuzz/fizzbuzz.scp-project \
--bootstrap "$SCP_BOOTSTRAP" --store /tmp/fizzbuzz/cas --output /tmp/fizzbuzz/program.prepared --seconds 240
result/bin/tricu scp run /tmp/fizzbuzz/program.prepared \
--input-tree-file demos/consumers/fizzbuzz/fizzbuzz-input.tree --seconds 60
Its project configuration binds the pinned fizzbuzz entry to initial state
(2, 4) from fizzbuzz-phases.tree:
"configuration": {
"bind": "fizzbuzz",
"state": {"tree-file": "fizzbuzz-phases.tree"}
}
This shared operation publishes the worker under presentation.entry. For
computed configuration, use the existing entry, input, state form to run
an ordinary SCP configuration consumer. Both use the same worker, invocation,
and receipt publication authority; the receipt records which profile was used.
The runtime input is the tree list 1 through 15. The FizzBuzz
mini-course derives the clocks from
first principles and follows this real build/run boundary.
Author tree data
Define ordinary values in a .tri file, then select each definition explicitly:
result/bin/tricu tree write demos/consumers/fizzbuzz/fizzbuzz-data.tri \
--name phases --output demos/consumers/fizzbuzz/fizzbuzz-phases.tree --store /tmp/fizzbuzz/authoring
result/bin/tricu tree write demos/consumers/fizzbuzz/fizzbuzz-data.tri \
--name input --output demos/consumers/fizzbuzz/fizzbuzz-input.tree --store /tmp/fizzbuzz/authoring
tree write evaluates source at this authoring boundary and writes the selected
value through the existing tree-term codec. It runs no IO action. Output
replaces atomically after successful evaluation/encoding, preserves an existing
file's mode, and refuses source overwrite and symbolic-link destinations. Its
store supports normal imports during authoring. Runtime arguments remain
serialized data and source-free invocation never consults this authoring file.
Configuration input/state also accept tree-file (relative payload path) or
tree-pin (hash in the selected CAS). Unlike file, these decode an exact tree
rather than constructing a byte-list tree. The consumer's contract interprets
the resulting tree, including Leaf, lists, strings, numbers and workers.
Both transports reuse arboricx.tree-term.v1: byte 0 is Leaf, byte 1 precedes a
Stem child, byte 2 precedes Fork left/right children. Truncated, unknown-tag and
trailing-byte payloads fail decoding; CAS inputs additionally verify the digest
under that domain. Raw files contain complete tree-term payloads. Decoding
constructs the argument tree. Normal consumer computation may inspect or
explicitly invoke values according to its contract.
Native CAS execution accepts ordered, repeatable --arg TEXT, --arg-tree-file FILE, and --arg-tree-pin HASH arguments. Prepared direct programs accept one
tree through --input-tree-file or --input-tree-pin; file presentation uses
--input-file. Direct runs default to --format result: ok: VALUE or error: DETAIL. Successful Leaf payloads display as ok: []; other payloads use the
decoder's conventions (Leaf also encodes zero). Use --format tree to display
the complete exact Result, or --format decode for the earlier whole-tree
decoder view. The native equivalent of Result display is arboricx run ... --result --format decode. Native CAS runs accept --profile +RTS -T -RTS:
JSON samples on stderr delimit loading, each invocation, and numbered IO yields.
Each sample performs a major GC and reports monotonic time, cumulative allocation,
GC time, and live managed-heap bytes. Yield samples also report native Bind/Local
frame counts and scheduler task count. Invocation completion forces the entire
result tree; output rendering follows its sample. Loading includes manifest,
code, argument decoding, and root evaluation; remaining lazy work occurs during
invocation. Samples include collection/recording overhead. A malformed Result envelope is a
presentation/protocol error; canonical failure Results remain data with exit
status zero. Arbitrary error trees survive and fall back to the selected view.
This is display only; file presentation owns its own output. The direct prepared
record selects the existing invocation manifest/export.
prepare uses separate bounded build, configuration and publication processes.
Build/configuration commands load trusted workspace exports directly, without
writing generated source into the checkout. --bootstrap DIRECTORY selects the
workspace for these phase exports; by default it uses the current workspace.
Prepared run loads no workspace or library source. GNU timeout supplies the
process deadline and five-second TERM/KILL escalation. It generates a
conservative runner-declared compiler identity; --compiler overrides it
explicitly. Only successful completion replaces the output record via one atomic
rename. Failed preparation may leave CAS objects, but preserves an existing
record. The record selects the existing CAS manifests/receipts.
run reads only that record and invokes pinned native CAS code. It never loads
the project declaration, rebuilds or reconfigures. Store paths are relative to
the record: move the record and CAS together, preserving their layout. Treat
records as trusted selections. An edited declaration has no effect on previously
prepared runs until you explicitly prepare again.
Advanced phase commands
For separate phase inspection/control, from the repository root:
# Runner-declared compiler identity.
compiler=$({ sha256sum result/bin/tricu tricu.workspace;
find lib -name '*.tri' -type f -print0 | sort -z | xargs -0 sha256sum;
} | sha256sum | cut -d ' ' -f 1)
result/bin/tricu scp build --store /tmp/tricu-logs --seconds 240 \
--workspace demos.consumer-prefix-workspace --directory "$PWD" --compiler "$compiler"
printf 'ERROR ' > /tmp/log-prefix
result/bin/tricu scp configure --store /tmp/tricu-logs --seconds 240 \
--code CODE_MANIFEST_HASH --entry configurePrefix \
--input-file /tmp/log-prefix --state-leaf
result/bin/tricu scp file --store /tmp/tricu-logs --seconds 240 \
--invocations INVOCATIONS_HASH --entry prefixLines --label matches
result/bin/tricu scp run-file --store /tmp/tricu-logs --seconds 60 \
--manifest FILE_MANIFEST_HASH --input-file "$PWD/demos/consumers/prefix-lines/tagged-log.fixture"
# matches=3, first=disk; Result payload is ["disk", t, "final"]
Each command prints the pins used by the next step. Build code once, then
configure different prefixes from that same code manifest. Prefix files are
exact byte data: printf, not echo, avoids an unintended newline. Neither
input nor state has an implicit default: choose --input-leaf/--input-file
and --state-leaf/--state-file, or the explicit tree-file/tree-pin choices.
Unqualified --input-file/--state-file values are byte lists. Use
tree-file/tree-pin for exact serialized trees. Arbitrary tree requests remain
available through the portable
build-driver API.
For simple entry/state binding without a configuration consumer:
result/bin/tricu scp configure-entry --store /tmp/fizzbuzz/cas --seconds 240 \
--code CODE_MANIFEST_HASH --entry fizzbuzz --name fizzbuzz \
--state-tree-file demos/consumers/fizzbuzz/fizzbuzz-phases.tree
Its portable request is entryConfigurationRequest manifest entry name state;
bindPinned verifies the pinned export, constructs its worker, and publishes
through the standard configuration authority. The receipt binds the entry,
publication name (input object), state, manifests, and explicit configuration
profile scp-configuration.entry-state.v1. Configuration input is otherwise
reserved for the ordinary computation profile.
file explicitly selects the bytes → Result [line] presentation profile;
callable compatibility is trusted, not inferred from tree shape. run-file
loads its pinned processFile export, grants read access to the supplied path,
and invokes native CAS execution without source, bootstrap aliases or a
compiler. Read/consumer failures print failure-stage and readable
failure-detail; non-text errors stay in the returned Result. Application
failures return Result data with exit status zero. Native/process/protocol
failures and external deadlines produce nonzero command status (timeout: 124).
Build/configure/publication execute trusted local IO. --seconds bounds the
child process, including bootstrap and publication. Argument preparation occurs
before that deadline. Publication is atomic per object; timeout/failure may
leave intermediate CAS objects. Commands default to their own executable;
--tricu explicitly overrides the child runner. GNU timeout is required.
Paths travel as argument values; configuration files are snapshotted before
execution and data is applied as trees, with no source interpolation.
Input/store paths may contain spaces or quotes. The demo shell scripts also
accept TRICU to select which CLI executable they invoke.
Author a program
- Write native
.scpdefinitions with qualified!importdeclarations and exports.Self.namedenotes a local entry; configure its worker explicitly. - Supply a trusted workspace module exporting
request directory compiler.prefix-workspace.triis a narrow eight-unit example. Register its alias intricu.workspaceor import its pinned module manifest via--workspace. Declare dependencies, frontend profiles and configuration entries explicitly. - Logical names map explicitly to relative paths and
tri/scpfrontends. Paths may traverse parents; use trusted workspace maps. - Consumer input/state/result contracts live beside the definitions in
lib/consumer/. Select individual units or the full barrel deliberately; the graph builds declared dependencies, not just symbols eventually called.
These examples consume whole byte lists. Source, code, configured outputs and
receipts have distinct identities. Dynamic executable values require trusted,
ABI-compatible code. The demos' shell scripts remain advanced examples composing
the phase commands: build-prefix-lines.sh, build-data-lines.sh, and
build-configurations.sh. Examples are indexed in
demos/consumers/README.md.
Format source
The Haskell command runs the tricu library consumers.format; it does not
compile or resolve the SCP program being formatted. Run from this workspace,
using a fresh store while changing the formatting library:
result/bin/tricu scp format lib/consumer/add.scp --bootstrap "$SCP_BOOTSTRAP"
result/bin/tricu scp format path/to/program.scp --bootstrap "$SCP_BOOTSTRAP" --write
result/bin/tricu scp format path/to/program.scp --bootstrap "$SCP_BOOTSTRAP" --check
python3 test/scp_format_check.py result/bin/tricu --bootstrap "$SCP_BOOTSTRAP"
Without a mode flag, formatted UTF-8 source goes to stdout. --check exits
nonzero when the source needs formatting; --write replaces a changed file
atomically, preserving its POSIX mode bits. The flags are mutually exclusive;
write mode refuses symbolic links. Syntax/encoding failures do not replace the
file. An installed consumers.format store alias can also supply the library
outside the checkout; normal workspace/store import rules apply.
Formatting aligns rule-head @ and =>, plus corresponding call ... as,
recur ... @, become ... @, and route-resumption @ separators. Consumer
boundaries and blank lines separate column groups. Structural syntax paths keep
nested separators distinct. Rule indentation is two spaces. Headers, comments,
literal spellings, existing line breaks (including CRLF), and final newline
presence are preserved. Other expression spacing and line wrapping are retained.
Widths count characters, not terminal display cells; internal tabs and wide
Unicode glyphs can therefore look misaligned. The syntax walker recognizes
general SCP and the bounded registry-route spelling; it does not perform binding
validation or validate/resolve module headers.
The focused formatter test checks token/comment preservation, idempotence,
add.scp columns, nested/multiline cases, and CLI write/check/error behavior.
Run it as an opt-in focused check.
Library surface and ownership
The normal entry point is a project declaration and its prepared record. Library files are organized by responsibility, independent of source language:
lib/*.tri: general-purpose Tree Calculus libraries.lib/source/: tricu frontend/compiler and shared module/CAS infrastructure.lib/arboricx/: artifact formats and services.lib/consumer/*.scp: core authored SCP entries and the explicitbase.scpbarrel; text-specific entries live inlib/consumer/text/. Reusable effectful helpers and their package factories live together inlib/consumer/effects/.lib/consumer/compiler/: lexing, parsing, validation and lexical lowering.lib/consumer/runtime/: authored IR, independent interpreters, effects and file invocation/presentation.lib/consumer/build/: native headers, project phases, configuration and publication;tools/holds formatting and single-source bootstrap runners.lib/consumer/registry/andtext/: specialized application adapters.lib/consumer/experiments/: layout/FizzBuzz IR specimens, alternativelowerings/and earlier pinned-sourcelinking/;comparison/holds lambda baselines.
Semantic consumers.* module names are independent of these physical paths;
tricu.workspace is the explicit alias-to-source map. Native SCP workspace rows
likewise map logical names to exact paths; a subdirectory is not an implicit
namespace or dependency search path.
For library work, the import surface is organized by responsibility:
| Responsibility | Modules / intended entry points |
|---|---|
| Native consumer code | lib/consumer/*.scp and text/*.scp; each declares its exports. base.scp explicitly re-exports the full collection. Names denote entries, not configured workers. |
| Portable phase API | consumers.build-driver: workspaceBuildRequest, configurationRequest, buildCode, configurePinned, and result accessors. consumers.configure validates/publishes returned worker rows. |
| Compilation | consumers.source (lexer), base-source (single consumer), unit-source (definitions), unit-compile (static references), unit-build (native headers/graph profile), lexical-worker (validated worker lowering). pattern-slots is a small compiler-internal ordering helper. |
| Semantics and checks | consumers.structural owns authored IR and the independent oracle; validate checks IR; linked-reference matches exact registered entries without executing compiled code. |
| Execution ABI | consumers.lexical-worker: lCompile, lCompileOpen, lRunChecked, lRunOpenChecked, worker construction/shape helpers. Open calls receive explicit state. |
| IO / tools | consumers.file owns file reads and Result presentation; command owns phase actions/output markers; format owns source alignment; effect dispatches explicit operation/receiver registries. effect-compose experimentally interprets explicit helper-return frames; file-effects supplies shared ReadBytes/WriteBytes handlers; copy-bytes owns the reusable conversion helper's package factory. source-worker supplies explicit compileSource bytes initial / loadSource path initial bootstrap for single-source lessons. |
| Reusable exploratory adapters | consumers.layout constructs authored layout transitions; registry, registry-base, registry-path, registry-source, and text-summary provide application/effect boundaries and bounded route syntax used by experiments. |
| Comparison implementations | consumers.comparison.registry-lambda and consumers.comparison.text-summary-lambda share reusable baseline code under lib/consumer/comparison/. |
| Alternative lowering experiments | consumers.lowerings.{specialize,project,success,tail-fold,cps-worker} live under lib/consumer/experiments/lowerings/ as comparison/research modules. fizzbuzz is an authored-IR fixture. |
| Exploratory pinned source linking | consumers.module and link retain the earlier explicit hash-pinned linker/preparation probes. Native project builds use build-driver and the shared graph instead. |
Names abbreviated in the table share the consumers. prefix. The neutral
source.build, source.module, source.object, lock/receipt and CAS layers
retain publication/verification authority. source.workspace-build deliberately
assembles the mixed .tri/SCP profiles.
.tri workspace aliases expose definitions; they do not enforce a private
helper boundary. Treat the entry points above as the intended interfaces, not
every bs*/us*/uc*/ub*/l* helper as a compatibility promise. Native
.scp exports and selected CAS publication lists are actual export boundaries.
Application-specific workspace maps/configurations belong in demos/. Shared
loaders, counters, handlers and renderers live in the library. Lambda baselines
use consumers.comparison.*; alternative lowerings remain explicit experiments.
Their progression and historical measurements are collected in the lowering
experiments.
Focused checks are in validation. Historical lowering and effect experiments are not part of ordinary project preparation.