I have never found “it can print Hello World” to be convincing evidence that a language is useful.
For a systems scripting language such as HHY, the better question is whether it can carry a complete job involving files, subprocesses, HTTP, bounded concurrency, failure isolation, structured output, and stable exit codes. FlowGuard is my first practical answer.
It accepts a project directory, JSON configuration, and report path. It checks required files, scans file size and possible credentials, runs quality commands and service checks concurrently, then atomically writes JSON. No failures means exit code 0; a failed gate returns 1; invalid usage returns 3.
A quality gate is not a list of commands. It is a workflow that must finish collecting facts even when individual checks fail.

Keep orchestration at the entry point
flowguard.hhy does not own every detail. Six modules separate structure, files, commands, health, shared checks, and reporting. The entry point composes them:
let structure_checks = inspect_structure(root, config.required_files)
let file_checks = inspect_files(root, config.limits.large_file)
let command_checks = inspect_commands(root, config.commands)
let health_checks = inspect_health(config.health_checks)
let checks = combine([structure_checks, file_checks, command_checks, health_checks])
let report = build_report(config.project.name, args[0], checks)
report
|> encode_json({ pretty: true })
|> save_text(output_path, { atomic: true })
This is the shape I want HHY to express: inputs, transformations, effects, and the result boundary remain visible. Modules exist so each class of check can evolve and be tested independently, not to turn a small program into ceremony.
attempt matters more than fail-fast
A gate that exits on the first failed command provides poor diagnostics. CI may report a failed test but never reach the missing license, unhealthy endpoint, or credential finding.
FlowGuard wraps each external command in attempt, converting execution failure into data. HTTP checks use the same model. A failure becomes a structured status: "fail" record instead of terminating the report.
let execution = attempt {
run(command.argv, {
cwd: root,
timeout: 15s,
max_output: 1mib
})
}
Arguments go directly to run as an argv array, never through a constructed shell command. Every command has a fifteen-second timeout and a 1 MiB output ceiling. The useful property is not shorter syntax; it is that injection, runaway execution, and unbounded output are outside the default path.
Bound concurrency and redact findings
Quality commands use parallel(3) and health checks use parallel(4). These tasks mostly wait on processes or networks. Bounded concurrency reduces wall time without allowing a larger configuration to exhaust the machine.
The scanner recognizes patterns such as private-key headers, DEMO_TOKEN=, and password=, but stores only a filename and content_redacted: true. A tool may need to detect a secret; that does not give it permission to reproduce one.
File limits use real Bytes values. Configuration strings such as 256b, 1kib, 4kib, and 1mib are parsed before comparison, keeping units in the type and out of naming conventions.
A gate must test its failure path
The end-to-end test includes two deterministic repositories. healthy-project has required files, successful commands, and a 2xx health endpoint; all eight checks pass. risky-project deliberately omits LICENSE, includes a fake DEMO_TOKEN, runs a failing command, and requests a 404 endpoint.
Exit code 1 in the second scenario is a correct result. The harness then reads the report and verifies that all five failures exist and no matching credential content escaped.

sh practical-projects/flowguard/self-test.sh
What the project proved
FlowGuard confirmed where HHY is most distinct. It is not trying to replace one shell command. It gives files, processes, HTTP, and streams one readable model for errors and resource limits.
Shell remains excellent for connecting a few happy-path commands. Once a job requires bounded concurrency, error aggregation, redacted reports, atomic writes, and stable exit codes, the script is becoming an application. HHY is meant to keep that transition direct.