Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ea0d434
feat(runtime-core): admit stopped clean development owners within res…
roodboi Oct 19, 2026
8847da4
fix(env): keep stored ciphertext when a secret value is unchanged
roodboi Oct 19, 2026
cc140c8
feat(agents): shared AX target and result contract with a read-only i…
roodboi Oct 19, 2026
77c599a
fix(env): ignore orphaned env staging files and pin atomic write beha…
roodboi Oct 19, 2026
ad65a7e
chore(hack): refresh the managed .hack/.gitignore block
roodboi Oct 19, 2026
bc49c11
fix(agents): refuse malformed Compose files and cover target selectio…
roodboi Oct 19, 2026
6698f47
fix(runtime-core): require a completed down for the retained reserve
roodboi Oct 19, 2026
52f0ffc
test(env): encrypt ciphertext-only fixtures in a fresh store
roodboi Oct 19, 2026
ee3704f
feat(agents): read-only reviewed task catalog
roodboi Oct 19, 2026
a3eb34e
docs(env): add hack project target to the command and delivery matrix
roodboi Oct 19, 2026
c3ccabd
fix(runtime-core): gate admission test accessors to macOS tests
roodboi Oct 19, 2026
267f62b
fix(agents): close task catalog inline-command and pin-path bypasses
roodboi Oct 19, 2026
5d58e9b
fix(env): refuse to replace an env layer that cannot be read or written
roodboi Oct 19, 2026
da79896
fix(runtime-core): use the retained reserve only when a restart needs it
roodboi Oct 19, 2026
356f6e3
fix(agents): require tracked task pins and cover legacy and runner ev…
roodboi Oct 19, 2026
aab02f8
test(agents): allowlist the task catalog's synthetic secret canary
roodboi Oct 19, 2026
b82eb4e
Merge wf/f17-ax-contracts-b16 into wf/f18-task-catalog-b16
roodboi Oct 19, 2026
2dd831e
docs(env): add hack project task list and inspect to the delivery matrix
roodboi Oct 19, 2026
5352415
fix(agents): close unreadable-input faults in the target inspector
roodboi Oct 19, 2026
6400eec
Merge wf/f17-ax-contracts-b16 into wf/f18-task-catalog-b16
roodboi Oct 20, 2026
82d3ffa
docs(runtime-core): say runtime probe reports the reserve basis it qu…
roodboi Oct 20, 2026
bb8f740
Merge next into wf/f7-stable-ciphertext-b16
roodboi Oct 20, 2026
8395b34
Merge remote-tracking branch 'origin/next' into mn-wf-f17-ax-contract…
roodboi Oct 20, 2026
7824efd
Merge wf/f17-ax-contracts-b16 into wf/f18-task-catalog-b16
roodboi Oct 20, 2026
125e212
fix(agents): refuse data: URL modules, git bisect/filter-branch and j…
roodboi Oct 20, 2026
96ae39e
fix(runtime-core): make start admission steps impossible to skip
roodboi Oct 20, 2026
2d0f92c
Merge next into wf/f7-stable-ciphertext-b16
roodboi Oct 20, 2026
e99f68c
test(env): cover the refusal when a layer's owner cannot be kept
roodboi Oct 20, 2026
c733698
Merge origin/next into wf/f18-task-catalog-b16
roodboi Oct 21, 2026
8b649e0
docs(runtime-core): say the start admission sequence is typed
roodboi Oct 21, 2026
14676d5
fix(agents): refuse git remote-side command options and dash-versione…
roodboi Oct 21, 2026
268b9ee
train: merge #344
roodboi Oct 21, 2026
65c7ddc
train: merge #340
roodboi Oct 21, 2026
af1f8f6
train: merge #353
roodboi Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@ condition = "AND"
paths = ['''^tests/mcp-inspect-tools\.test\.ts$''']
regexTarget = "line"
regexes = ['''(?m)^\s*const SECRET_CANARY = "zqx-secret-canary-7731";\s*$''']

[[rules.allowlists]]
description = "Task catalog tests use a fixed synthetic canary to prove smuggled env values are never echoed"
condition = "AND"
paths = ['''^tests/project-task-catalog\.test\.ts$''']
regexTarget = "line"
regexes = ['''(?m)^\s*const SECRET_CANARY = "secret-canary-91c4d0";\s*$''']
1 change: 1 addition & 0 deletions .hack/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
.env.state.json
hack.env.local.yaml
hack.env.*.local.yaml
.hack.env*.tmp
tickets/
# end managed by hack
172 changes: 172 additions & 0 deletions .hack/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
{
"schema": "hack.tasks.v0",
"tasks": [
{
"id": "models",
"summary": "Check the repository's TLA+ models with TLC in the toolchain service.",
"runner": {
"kind": "compose_service",
"service": "toolchain",
"definition_sha256": "5eb5bd4b52eaa878821fd2929292a5c7a5df0bb6524843e0843addd7404fd767"
},
"argv": ["models"],
"workdir": null,
"env": { "overlay": null, "keys": [] },
"requires": ["checkout_read", "network_egress"],
"effects": [
"reads_checkout",
"network_egress",
"creates_ephemeral_container"
],
"prerequisites": [
"A container engine that Hack can reach.",
"The toolchain image builds from .hack/toolchain/Dockerfile; the first build downloads pinned tools."
],
"checks": [
"Exit status 0 means every model in scripts/check-tla-models.ts passed TLC."
],
"cleanup": [
"The one-shot container exits; TLC state lives in the container's temporary directory.",
"Named toolchain volumes are kept for reuse."
],
"provenance": {
"source": "repository_script",
"inputs": [
{
"path": ".hack/toolchain/run.sh",
"sha256": "0690919e0937c49638ea1c8318d731df3c45df26c3865c6a37ae263e8854e6a5"
},
{
"path": ".hack/toolchain/Dockerfile",
"sha256": "26743556cde8930e2105e860a9b6bd3f5d6d4e2a4f5b567df01d4dd3e8e0c745"
},
{
"path": "mise.toml",
"sha256": "e43a4542f0c4a466219402a1f5b9ad993bf4a6b109cadfba9281149de9a2f205"
},
{
"path": "package.json",
"pointer": "/scripts/test:models",
"sha256": "69aff6d9215e0567db0cb729b6f6f227f0482092d3561e813d621cb4d49a0b54"
},
{
"path": "scripts/check-tla-models.ts",
"sha256": "e162839a788a69c6c42adcfb28f12aa91943a5f43b2de43c445ea96e88b5cc81"
}
],
"review": "HACK-1157"
}
},
{
"id": "check",
"summary": "Install locked dependencies, then run typecheck and lint/format checks in the toolchain service.",
"runner": {
"kind": "compose_service",
"service": "toolchain",
"definition_sha256": "5eb5bd4b52eaa878821fd2929292a5c7a5df0bb6524843e0843addd7404fd767"
},
"argv": ["check"],
"workdir": null,
"env": { "overlay": null, "keys": [] },
"requires": [
"checkout_read",
"checkout_write",
"named_volumes",
"network_egress"
],
"effects": [
"reads_checkout",
"writes_checkout",
"writes_named_volumes",
"network_egress",
"creates_ephemeral_container"
],
"prerequisites": [
"A container engine that Hack can reach.",
"The toolchain image builds from .hack/toolchain/Dockerfile; the first build downloads pinned tools."
],
"checks": [
"Exit status 0 means typecheck and the repository check script passed."
],
"cleanup": [
"The one-shot container exits; installed dependencies stay in the toolchain_dependencies volume.",
"Build tool caches written under the checkout are ignored by Git."
],
"provenance": {
"source": "repository_script",
"inputs": [
{
"path": ".hack/toolchain/run.sh",
"sha256": "0690919e0937c49638ea1c8318d731df3c45df26c3865c6a37ae263e8854e6a5"
},
{
"path": ".hack/toolchain/Dockerfile",
"sha256": "26743556cde8930e2105e860a9b6bd3f5d6d4e2a4f5b567df01d4dd3e8e0c745"
},
{
"path": "mise.toml",
"sha256": "e43a4542f0c4a466219402a1f5b9ad993bf4a6b109cadfba9281149de9a2f205"
},
{
"path": "package.json",
"pointer": "/scripts/check",
"sha256": "ae7e24ee743b2aa230c6a492da63a0e7b6a0db925e691a24a7a8f1c97d3ee7fb"
},
{
"path": "package.json",
"pointer": "/scripts/typecheck",
"sha256": "4209d96797d84dcd31f559acfde1395b9969d025ef6fc11f4684567a3009ef6f"
}
],
"review": "HACK-1157"
}
},
{
"id": "rust-check",
"summary": "Check formatting and run Clippy for the runtime-core crate in the toolchain service.",
"runner": {
"kind": "compose_service",
"service": "toolchain",
"definition_sha256": "5eb5bd4b52eaa878821fd2929292a5c7a5df0bb6524843e0843addd7404fd767"
},
"argv": ["rust-check"],
"workdir": null,
"env": { "overlay": null, "keys": [] },
"requires": ["checkout_read", "named_volumes", "network_egress"],
"effects": [
"reads_checkout",
"writes_named_volumes",
"network_egress",
"creates_ephemeral_container"
],
"prerequisites": [
"A container engine that Hack can reach.",
"The toolchain image builds from .hack/toolchain/Dockerfile; the first build downloads pinned tools."
],
"checks": [
"Exit status 0 means cargo fmt --check and Clippy with -D warnings passed."
],
"cleanup": [
"The one-shot container exits; the Cargo registry and build outputs stay in named toolchain volumes."
],
"provenance": {
"source": "repository_script",
"inputs": [
{
"path": ".hack/toolchain/run.sh",
"sha256": "0690919e0937c49638ea1c8318d731df3c45df26c3865c6a37ae263e8854e6a5"
},
{
"path": ".hack/toolchain/Dockerfile",
"sha256": "26743556cde8930e2105e860a9b6bd3f5d6d4e2a4f5b567df01d4dd3e8e0c745"
},
{
"path": "mise.toml",
"sha256": "e43a4542f0c4a466219402a1f5b9ad993bf4a6b109cadfba9281149de9a2f205"
}
],
"review": "HACK-1157"
}
}
]
}
34 changes: 34 additions & 0 deletions docs/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ worktrees inherit the rules with zero setup. It covers (patterns relative to
- `.env.state.json`
- `hack.env.local.yaml`
- `hack.env.*.local.yaml`
- `.hack.env*.tmp` (staging files an interrupted env write can leave behind)
- `tickets/` (retained only to contain retired machine-local ticket caches during upgrades)

How it is maintained:
Expand Down Expand Up @@ -341,6 +342,8 @@ into a host command, and `--target` on env and host commands chooses the `host`
| `hack config validate` | `--env` | Per invocation; resolves the native selection without reading managed env documents. | Not needed. | Authored native projects only; cannot be combined with `--file`. |
| `hack config plan` | `--env` | Per invocation; metadata only (names, scopes, secret flags), never decrypted. | Not needed. | Authored native projects only. |
| `hack project target` | `--env`, `--service` | Per invocation, read-only; reports the overlay selection (explicit, project default or base) without reading env files or values. | Not needed. | Selection only: overlay existence is not checked; no runtime required. |
| `hack project task list` | `--env` | Per invocation, read-only; selects the overlay that each recipe's reviewed env scope is compared with, without reading env files or values. | Not needed. | Selection only; lists recipes and runs nothing, so no runtime is required. |
| `hack project task inspect` | `--env` | Per invocation, read-only; as for `hack project task list`, for one recipe. | Not needed. | Selection only; runs nothing, so no runtime is required. |
| `hack session start` | `--env`, `--service` | When the workspace is created; the name gains `.env-<name>` (`.env-base` for base spellings), and `--up` runs `hack up -d` with the same selection. | Start a new workspace or pass `--env` to `hack session exec`. | Injects into the workspace shell, not into containers. |
| `hack session exec` | `--env`, `--service` | Per command; omitted reuses the selection encoded in the workspace name. | Each command resolves again. | Injects into the workspace command, not into containers. |

Expand Down Expand Up @@ -490,6 +493,37 @@ input and without writing anything:
`.hack/hack.env.<overlay>.local.yaml`) instead of the shared repo env file — use it for
per-checkout values you don't want to commit.

Repeating `hack env add` or `hack env set` with the value already stored is a no-op: when
the existing secret decrypts to the same value under the current key, its ciphertext is
kept, so once Hack has written the file it stays byte-identical and Git shows no change.
A hand-edited file (comments, other formatting, or no `global: {}`) is normalized on the
first repeat and stays byte-identical after that. A different value, a plain entry
becoming secret, or an entry that does not decrypt under the current key gets new
ciphertext with a fresh IV. Each command writes one key; a script that sets several keys
is not atomic across them.

Each env file write goes to a hidden `.<file>.<id>.tmp` file in the same directory and
replaces the file by rename, so an interrupted write leaves the previous file in place.
A write killed before the rename can leave that staging file behind. It holds the whole
new layer, so `.hack/.gitignore` ignores `.hack.env*.tmp`, and you can delete it. Hack
does not remove it on the next write, because another write may still be using it. The
rename has these effects:

- a symlinked env file keeps its link; the new contents land at the file the link points
to, and the staging file is created next to that file
- a symlink whose target does not exist is replaced by a regular file
- a hard link is broken: the env file gets a new inode, and any other name for the old
file keeps the previous contents
- the directory must be writable, even when the env file itself is writable

A rename does not need write access to the file it replaces, so Hack checks that access
itself before it stages anything. It refuses to replace an existing env file that it
cannot read, that is not a regular file, that is not writable, or whose owner and group
it cannot keep (for example a root-owned file left by an earlier `sudo hack`). The error
names the file and the reason, never a value, and the file is left as it was. Fix the
file's permissions or ownership and run the command again. A rewrite keeps the file's
permission bits, owner and group.

If you omit `key`, `value`, or `--service`, `hack env add` prompts for them interactively.
Pass `--no-interactive` (or set `HACK_NO_INTERACTIVE=1`) for scripted/agent usage: the
command applies documented defaults or fails fast with `E_INTERACTIVE_REQUIRED` instead of
Expand Down
20 changes: 14 additions & 6 deletions docs/guides/native-candidate.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,20 @@ v4 state is enabled. Runtime startup is a further explicit operation subject to
host resource admission. Read `--help` for the candidate command surface.

The development profile reserves the declared 32 GiB storage disk and 10 GiB
overlay ceiling plus 16 GiB of host disk space before startup. Its unchanged memory
budget is a 10 GiB free-plus-file-cache estimate: 6 GiB guest, 2 GiB provisional
provider overhead and 2 GiB host reserve. Normal and warning macOS pressure can
qualify only with that headroom, normal thermal/performance state and stable
swapouts. Development samples before the startup lease and again under the lease
before allocation, keeping the same swapout baseline across both windows.
overlay ceiling plus 16 GiB of host disk space before a fresh startup. Starting
again on a verified running pool, or restarting an exactly clean stopped pool whose
last `down` completed, reuses existing disks and reserves only the 16 GiB host
reserve; `runtime probe --profile development --json` reports the basis in
`disk_budget_basis`, and `retained_reserve_refusal` names why a stopped pool's
disks were charged again. A restart uses that reserve only when free disk is
below the fresh budget; with enough room it starts on the fresh budget as before.
Any other stopped pool keeps the fresh budget. The
unchanged memory budget is a 10 GiB free-plus-file-cache estimate: 6 GiB guest,
2 GiB provisional provider overhead and 2 GiB host reserve. Normal and warning
macOS pressure can qualify only with that headroom, normal thermal/performance
state and stable swapouts. Development samples before the startup lease and again
under the lease before allocation, keeping the same swapout baseline across both
windows.
Critical, unknown or incomplete observations refuse; there is no bypass option.
It reports host load without refusing solely because unrelated jobs raise the
one-minute average. The research profile keeps its normal-only pressure policy,
Expand Down
1 change: 1 addition & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@
- [MCP tool contract](./reference/mcp-tools.md) — every MCP tool: annotations, inputs, argv, results, and the value-free env tools
- [MCP logs contract](./reference/mcp-logs.md) — cutoff versus backend failure for the MCP log tools, and the cursor/handle decision
- [AX target and result contract](./reference/ax-target-result-contract.md) — provisional target schema, closed refusals and the read-only `hack project target` inspector
- [AX task catalog contract](./reference/ax-task-catalog.md) — provisional reviewed-recipe catalog, closed refusals and the read-only `hack project task list|inspect` commands
Loading
Loading