tri-tools.md (20019B)
Compile, run, and format
Build and run sets up the included stable compiler and a first
application. The commands below use its WORK variable.
Language and Modules define source behavior.
Compile and evaluate
result/bin/tricu eval demos/source-patterns/app.tri \
--store "$WORK/application" --format number
# 23
result/bin/tricu arboricx compile --file demos/source-patterns/app.tri \
--store "$WORK/application" --name main --output "$WORK/app.arboricx"
Dependencies use sibling NAME.tri files plus the nearest tricu.workspace
source-location declaration; see workspace rules.
The module basename must be a valid workspace identifier.
eval accepts one file and selects exported main. compile packages all
published exports, or the subset selected by repeatable --name options. Missing
or duplicate selections fail.
Without --toolchain, the installed stable compiler is authenticated and loaded
indexed. Its bundle SHA256 identifies the compiler in receipts. The packaged
bundle is a Cabal data file, retained in the Nix closure even when invoked outside
the checkout. Relocatable installations can set tricu_datadir to their data
root (containing bootstrap/build-stable.arboricx); missing/corrupt data fails
closed. Updating the shipped compiler also requires updating its pinned host
SHA256.
--toolchain is an explicit full manifest hash with a sourceBuildGraph export.
--tool-store defaults to --store and requires --toolchain; separating them
lets applications start with an empty store. Explicit overrides retain their
verified executable-object hash as compiler identity in receipts.
When the file argument is omitted, eval requires a complete native module on
stdin, including its exported entrypoint. Stdin uses an isolated temporary
workspace: use manifest imports, or a file for workspace-relative dependencies.
Multiple input files are rejected; use native imports instead. tree write also
uses the packaged compiler and selects an exported definition.
--compile-timeout SECONDS bounds tool loading and module builds. Its default is
120 seconds, with a range of 1 to 86400. Completed immutable objects can remain
after an interrupted build.
Stable backend and direct self-recursion
bootstrap/build-stable.arboricx selects stable lowering for every source module
built by that artifact. It supports direct function self-recursion and uses the
source-module.stable.v1 options identity. Imported trees keep their exact
representations.
Compiler-generation backend selection is separate: generation runners use
--backend stable. The retained optimized application artifact is build.arboricx.
See Compiler architecture for its
abstraction and demand behavior, and Compiler rebuilding
for generation procedures.
Stored execution
result/bin/tricu arboricx run --module "$APP_MANIFEST" --name main \
--store "$WORK/runtime" --format number
Arguments are applied in option order:
| Option | Argument |
|---|---|
--arg TEXT |
A tricu string. |
--arg-tree-file FILE |
An exact tree-term byte payload. |
--arg-tree-pin HASH |
A verified flat tree-term object from the selected store. |
--arg-export MANIFEST:EXPORT |
A verified flat or indexed executable export. |
--format chooses display interpretation; --result displays a canonical Result
with success/error labels. --repeat repeats an invocation after loading once;
pure applications may reuse an already evaluated value, so this is unsuitable
for independent fresh-run benchmarks.
For stored IO commands that print their own output, --io --no-result
suppresses the trailing host-rendered return value. Program output and errors
are unchanged; --no-result without --io, or combined with --result, fails
before execution. Repeat/profiling markers retain their existing behavior.
Observation phases still occur with --no-result, leaving the suppressed return
value unrendered and lazy. Explicit --profile-full-result forcing remains
independent of result rendering.
Read-only interaction-tree inspection
Inspect a materialized host IO tree as read-only data directly from a bundle:
result/bin/tricu arboricx inspect --file "$WORK/app.arboricx" --name main --as io
result/bin/tricu arboricx inspect --file "$WORK/app.arboricx" --name main --as io --json
--as io is required and selects the tricuIO version 1 envelope and host
action protocol. Pure contract effects use a separate protocol. --name
defaults to main. Supply an already materialized IO tree; apply any arguments
needed to produce that tree before inspection. Only explicit child actions are
followed; continuations and environment-transforming functions remain opaque,
labeled by their roles. Names come from bundle export aliases, and the outline
covers currently explicit control flow.
For a small example with a named continuation:
result/bin/tricu arboricx compile --file demos/interaction-inspection.tri \
--store "$WORK/application" --output "$WORK/inspection.arboricx"
result/bin/tricu arboricx inspect --file "$WORK/inspection.arboricx" --as io
The outline includes these roles (node indices omitted here):
root: tricuIO v1 aliases: "main"
root.action: Bind
root.action.action: ReadFile
root.action.action.path: "settings.txt"
root.action.resume: continuation (not applied) aliases: "afterRead"
The demo spells out the IO constructors directly. Reading settings.txt occurs
only when the IO tree is executed; compilation and inspection treat its path as
data.
Inspect the actual Arboricx server
demos/arboricx-inspection/main.tri
reexports the application's exact main tree and adds named inspection views
in a separate companion module:
result/bin/tricu arboricx compile --file demos/arboricx-inspection/main.tri \
--store "$WORK/application" --output "$WORK/server-inspection.arboricx" \
--compile-timeout 900
result/bin/tricu arboricx inspect --file "$WORK/server-inspection.arboricx" --as io
result/bin/tricu arboricx inspect --file "$WORK/server-inspection.arboricx" \
--name serveView --as io
The main outline names every currently visible occurrence:
main: tricuIO v1
startupAction: Bind
initializeThenServe: Bind
initializeStore: Bind
ensureRootDirectory: Bind
createRootDirectory: CreateDirectory
storeRoot: "./store"
afterCreateDirectory: continuation (not applied)
afterRootDirectory: continuation (not applied)
afterInitialization: continuation (not applied)
finishStartup: continuation (not applied)
The outline uses export aliases in place of occurrence paths for readability.
Four configured values match directly: storeRoot, createRootDirectory,
ensureRootDirectory, and initializeStore. The remaining labels are
author-named structural projections of the materialized action. Projection
extracts existing subtrees as data; all four continuations remain opaque and
unapplied. Aliases establish exact tree identity; their names convey the
author's interpretation. Matching requires exact structural equality with the
specialized tree. General library helpers and observationally equivalent
expressions can have different trees.
The independently materialized serveView exposes serveRegistry, its
createSocket action, and the opaque afterSocketResult continuation.
Bind/listen/accept remain behind that continuation and depend on the returned
socket. initializeView offers a shorter view of store initialization. Inspection
reads these trees; running the view exports with --io performs their effects.
Run the comparison in a new directory:
python3 test/arboricx_server_inspection.py result/bin/tricu \
--directory /tmp/server-inspection
The harness checks exact main identity, compares ordinary exports with
structural projections, and saves JSON reports and outlines for the separate
views. It reports bundle bytes, stored nodes, alias coverage, and compilation
timings for the selected compiler. Timings include sequential cache reuse.
Validation consists of compilation, artifact import, and read-only inspection;
application IO remains unexecuted.
Identities, output, and limits
The bundle’s whole-file SHA-256 fingerprints the inspected bytes. Authenticate
that fingerprint against an independently selected pin and verify provenance
separately. The complete indexed graph is structurally validated, including
unused nodes. Export aliases are exact matches to displayed trees within that
bundle. Several names may match one tree, and names lost through specialization
cannot be recovered. n123 identifies a bundle-local tree node. CAS objects
have separate addresses. A generated path such as root.action.resume
identifies an occurrence; multiple occurrences of one tree can execute effects
independently.
--json emits tricu.interaction-inspection.v1: bundle fingerprint, selected
export, supported protocol version, limits, stored-node count, and a depth-first
occurrences array. Each occurrence includes its path, depth, role, tree ID,
export aliases, omitted-alias count, display summary, status, and optional
reference to a previously described action occurrence. Summaries are display
text whose wording may change; retain the artifact for exact payload data.
Status is decoded, opaque, invalid, unknown, truncated, or
reference. opaque marks a value whose internals remain unexamined.
Callability and type validity require separate checks.
| Option | Default | Meaning |
|---|---|---|
--max-bytes |
67108864 | Input byte cap, adjustable downward to 1 byte. |
--max-depth |
24 | Occurrence depth, 0–256; root depth is 0. |
--max-nodes |
500 | Displayed occurrences, 1–100000; stored-node count is separate. |
--preview-items |
80 | Characters, bytes, or aliases per occurrence, 1–4096. |
Numbers are inspected up to 64 encoded bits. String/byte previews validate only
the visited prefix; suffixes remain unexamined. Depth and preview truncation
appear on the affected occurrence; occurrenceLimitReached reports an omitted
traversal remainder. Alias omission is reported separately. Payload previews may
reveal secrets already present in the artifact; treat the unredacted inspection
output accordingly.
Input must be a regular file. Validation and name indexing process the stored DAG directly. Display limits bound report size, and the input cap bounds file bytes. Validation heap and elapsed time require external resource limits when inspecting untrusted artifacts.
Unknown versions/tags and encountered malformed protocol shapes produce a report
and a nonzero exit. Invalid bundles, missing exports, and input/option errors
fail before a report. Deliberate opacity and budget truncation are successful
reports with explicitly partial protocol coverage. Complete-program validity and
execution safety require separate analysis. Inspection uses the bundle alone and
ignores --store.
IO and permissions
Select --io for interaction-tree execution. Grant file access with repeatable
--allow-read PATH and --allow-write PATH options. --unsafe-io grants broad
access and belongs only in trusted, deliberately selected runs.
Compilation grants reads to the selected source directory and application store, and writes to the application store. Application execution uses its own grants. Path grants authorize access within trusted programs and store directories. Hostile-filesystem containment requires separate isolation.
Host artifact checks
Imports require normal-order Tree Calculus semantics and term exports with
arboricx.abi.tree.v1. Export names must be unique. Aliases are single filename
components: qualified and symbolic names are valid, while empty names, ./..,
separators, colons, and control characters are rejected. The complete alias set
is validated before alias publication; subsequent IO failures can leave a partial
update.
Typed object reads verify domain-separated digests before decoding. Use the whole-bundle SHA-256 for registry downloads, and module manifest hashes for CLI execution. Storage specifies these identities and trust boundaries.
Indexed and flat objects
Import defaults to indexed storage; --indexed makes that selection explicit.
This preserves sharing for native library imports, stored execution, and compiler
loading. Bundle export accepts indexed or flat objects. Use --flat only for
explicit flat-tree consumers or retained older native compilers.
Flat import checks aggregate expanded bytes before writing. Its default budget
is 64 MiB; set a positive --max-flat-bytes BYTES to change it. This is an output
budget, distinct from input size, heap use, and execution time. The included
compiler should be imported indexed.
Profiling
result/bin/tricu eval demos/source-patterns/app.tri \
--store "$WORK/application" --format number --compile-profile \
+RTS -T -s -RTS 2>"$WORK/profile.log"
JSON samples report monotonic wall_ns, allocation, live heap, and GC counters
to stderr. Differences between timestamps give stage timings. Samples read
existing counters; allocation/live values reflect the most recent collection.
-s adds process totals. Stored execution provides --profile with +RTS -T.
Report preparation, loading, compilation, execution, publication, and result
observation separately when comparing workloads.
Stored --profile follows the ordinary renderer's demand. invoke-end-N marks
completion of the IO driver or creation of a still-lazy pure result. Pure
evaluation can continue during observation. observe-begin-N / observe-end-N
bracket rendering and output; observe-end-N follows flushing stdout. Measure
invoke-begin-N through observe-end-N for invocation plus selected output,
including rendering/IO costs. Merged logs receive a separating stderr newline
before the final JSON sample.
For an explicitly different workload, add --profile-full-result (requires
--profile). Separate force-begin-N / force-end-N samples bracket
full-result forcing before rendering. This traversal memoizes completed physical
nodes within one invocation, preserving sharing during traversal. It still
demands fields that --result discards: infinite or partial results can diverge
or exhaust resources. Samples read existing GC counters; reduction counts and
peak memory require separate measurement and resource controls.
Previously, --profile implicitly forced the complete expanded result before
invoke-end. Those historical intervals measured a different workload; compare
samples within the same profiling mode. Active workload probes label
invocation-through-output measurements explicitly; use fresh processes for
independent application evaluation. --repeat permits reuse of an evaluated
application.
.tri equation alignment
tricu format runs a pinned formatTri program. Prepare the current formatter
using the included compiler and a new output directory:
FORMAT_DIRECTORY="$WORK/formatter"
python3 test/prepare_formatter.py result/bin/tricu --directory "$FORMAT_DIRECTORY"
FORMATTER_MANIFEST=$(python3 -c \
'import json,sys; print(json.load(open(sys.argv[1]))["formatter_manifest"])' \
"$FORMAT_DIRECTORY/report.json")
result/bin/tricu format path/to/source.tri \
--formatter "$FORMATTER_MANIFEST" --tool-store "$FORMAT_DIRECTORY/store"
Preparation builds native dependencies, checks the formatting corpus and CLI controls, and records source hashes and compiler identity. Its bounds are 8 GiB child heap, 900 seconds compiling the formatter, and 1200 seconds overall.
Default output is UTF-8 source on stdout. --check fails when formatting would
change bytes. --write replaces the file atomically, preserving permissions and
refusing symbolic links. Select one of those modes at a time. --seconds bounds
loading and formatting, defaulting to 60. Invalid UTF-8, bad artifacts, and
cancellation fail before replacement. See the
formatting contract for which groups are aligned.
Library workspaces and demos
The repository tricu.workspace maps native library aliases to source paths;
ordinary CLI compilation stages mapped sources automatically. Bootstrap source
maps remain a separate compiler-rebuilding protocol. To reuse already compiled
dependency manifests from a completed generation in a new DEMO_DIRECTORY:
python3 test/native_library_migration.py result/bin/tricu "$GENERATION" \
"$DEMO_DIRECTORY" --backend stable --library demos.equality
The helper preserves native source and binds complete dependency manifests.
Supply additional prepared dependencies with repeatable --dependencies, or
select libraries in dependency order. Import the resulting bundle indexed and
run its selected export as above. Command and batch budgets default to 20 minutes;
full-cohort work uses explicitly selected limits from Testing.
Interactive sessions
The default REPL and tricu repl use the portable compiler. The repl command
accepts the same compiler override/deadline options as eval. Import the
prelude explicitly when needed. Simple x = 7 definitions persist; x displays
a result without changing session source. Redefinition replaces the entire
previous equation group and recompiles all session definitions, so dependents
see the new definition: after x = 7, f ignored = x, and x = 8, f t
returns 8.
For multiline definitions, enter !def NAME, the complete definition/equation
group, then !end. The compiled local export set must match the named session
groups exactly: a block for x defining only y, or also defining an unrelated
name, is rejected without changing the session. For multiline or ambiguous
expressions, use !expr through !end. EOF cancels an unfinished block.
replResult is reserved for the session. The single-line classifier chooses a
submission form; the portable compiler parses and validates every submission.
!import accepts native import syntax, with workspace dependencies resolved
from the current directory. !load FILE compiles the file and opens its
exported interface by immutable manifest. !use MANIFEST [NAMESPACE] imports a
pinned interface; a module alias is resolved to its current pin when submitted.
Import collisions fail, preserving existing definitions. Use qualified imports
when names overlap. Temporary session modules are removed after each submission;
the current directory must be writable. CAS objects and receipts may remain.
Compilation, import and result-rendering errors preserve prior successful source
and completion state. Immutable CAS publications and external effects persist.
Expression results are displayed as values; IO execution requires a separate
run, and retaining a result requires an explicit definition. !reset clears
source/imports; !env lists local exported definitions. !format, !output,
!store, !clear, !help, and !exit remain available. After changing
!store, ensure the new store contains the pinned dependencies before building.
The legacy !name tree injection operation is retired. Import explicit module
interfaces to introduce existing trees into the session. CLI operations and
source tests use the portable compiler.
Focused CLI and REPL validation, using the selected compiler and existing fixtures:
python3 test/default_frontend.py result/bin/tricu
This opts into bounded CLI and interactive probes, including package failures, overrides, working-directory independence, current syntax and error rollback.