feat(cli): expose workdir-derived hashes via omac diagnose --hash - #266
Open
Retr0MrWave wants to merge 2 commits into
Open
feat(cli): expose workdir-derived hashes via omac diagnose --hash#266Retr0MrWave wants to merge 2 commits into
omac diagnose --hash#266Retr0MrWave wants to merge 2 commits into
Conversation
Refs #156 omac derives four different identifiers from a workdir -- the start and serve runtime dirs, the keychain secret scope, and the tool-cache scope -- and none of them was reachable from the CLI, so debugging a run meant reverse-engineering a log path or a cache directory by hand. Every value is read from the producer the runtime itself uses; nothing here recomputes a digest. That is what required the two refactors: - createRuntimeDir / createRuntimeDirServe inlined their sha256 inside functions that also os.RemoveAll their target, so a reporting path that called them would wipe a live session's logs and pids. The pure name derivations move to runtimedir.go and both creators call them. - describeCacheScope is lifted out of provenance's buildCacheView, so --hash=cache and `omac provenance` cannot disagree about which cache a workdir resolves to. Shape follows the issue's comment (a debugging flag, not a new top-level verb) and mirrors the existing --probe compute-and-exit precedent. Two deviations from the issue body: JSON is a stable envelope with a tmpdir field rather than a flat object, because the runtime/serve paths are relative to $TMPDIR and a shell whose TMPDIR differs from the agent's resolves a different, equally correct path; and text output is stanzas rather than a table, because the full 64-hex digests pad a table past 140 columns. The tests assert against the real producers rather than hardcoded digests, which is the drift guard the issue asks for: re-inlining a hash in createRuntimeDir fails them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Ilia Zhuravok <ilia.zhuravok@tngtech.com>
Refs #156 Review findings on the previous commit. The important one: os.TempDir() is the wrong answer in the one place the answer matters most. start.go and serve.go export TMPDIR=<sandboxTmp> to the inner agent, so an agent running `omac diagnose --hash=runtime` from inside the sandbox -- the audience the NONO_SANDBOX.md pointer was aimed at -- was handed <sandboxTmp>/omac-<hash>, which does not exist. The digest was right; only the join was wrong. $OMAC_SOCKET is unaffected (both modes set it to <rtDir>/bridge.sock on the host), so its parent is read as the live runtime dir when it names the same directory as the derived path. A session belonging to another workdir, or a start session while asking for the serve kind, does not hijack the result. Also: - Failed kinds in --hash=all wrote an "error" stanza to stdout and exited 0, so a caller scraping stdout for paths could pick it up. Text-mode errors now go to stderr; the JSON `error` field is unchanged, being structured. - `--hash somethingelse` advised "use --hash=somethingelse", which fails again with "unknown kind". The attached-form hint is now gated on the stray token actually being a kind. - CLI.md claimed the cache kind always hashes "v1:<domain>:<canonical>" and a symlink-resolved path; under the default global scope it hashes the constant "v1:shared" with no path at all, so two projects show identical digests. Documented per scope. - CLI.md claimed --hash=cache reports "the cache this workdir actually gets". Like provenance, it reports the CONFIG-resolved scope and does not see --cache-scope, --ephemeral-cache, or --no-sandbox. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Ilia Zhuravok <ilia.zhuravok@tngtech.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue: Closes #156
What
omac diagnose --hash[=runtime|serve|keychain|cache|all]prints the four identifiers omac derives from a workdir, in text or--json.createRuntimeDir/createRuntimeDirServeinlined moves tointernal/cli/runtimedir.go; both creators now call it. Onesha256of a workdir left in the package.describeCacheScopelifted out of provenance'sbuildCacheView, so--hash=cacheandomac provenancecannot disagree.Why
Four different workdir-derived identifiers, none reachable from the CLI — debugging a run meant reverse-engineering a log path or a cache directory by hand.
internal/cli/controlinfo.go:18-20states the gap outright.Shape follows the comment on #156 (a debugging flag, not a new top-level verb) rather than the issue body's
omac hash. It mirrors the existing--probecompute-and-exit precedent.How
The issue's hard requirement is single-source-of-truth, and that is what forced the refactors:
createRuntimeDir/createRuntimeDirServeos.RemoveAlltheir target, so a reporting path that called them would wipe a live session'slogs/andpids/. Only the pure derivation moved out; the creators keep their side effects.runtimeDirPath,serveRuntimeDirPath,keychain.WorkdirID,describeCacheScope→toolcache.Describe*. Nothing here recomputes a digest.Sandbox correctness (2nd commit, from self-review):
start.go:1011/serve.go:1499exportTMPDIR=<sandboxTmp>to the inner agent, so joiningos.TempDir()handed an in-sandbox agent a path that does not exist — precisely the audiencedocs/NONO_SANDBOX.mdpoints at.$OMAC_SOCKETis unaffected (socketPath = filepath.Join(rtDir, "bridge.sock"),start.go:713), so its parent is read as the live runtime dir when it names the same directory as the derived one. Guarded on the dir name, so another workdir's session — or astartsession while asking forserve— cannot hijack the answer.Two deliberate deviations from the issue body, both worth a look:
{workdir, tmpdir, entries[]}), not the issue's flat{...,"dir":...}. Single-kind scripting isjq -r '.entries[0].path'.tmpdiris load-bearing given the remap above.KIND/DIGEST/PATHtable past 140 columns.Verification
17 tests in
hash_test.go, all passing. They assert against the real producers, not hardcoded digests — the drift guard the issue asks for. Mutation-checked: re-inlining a different hash insidecreateRuntimeDirfailsTestDiagnoseHashRuntimeMatchesCreateRuntimeDirwith a clear message. (Mutating the shared helper correctly does not fail; both sides move together, which is the point.)Manual, against the built binary:
Error paths exercised by hand and by test: unknown kind, the
--hash runtimespace form, a stray non-kind argument, and an unresolvable cache scope in bothallmode (stderr + other kinds survive, exit 0) and single-kind mode (stderr, exit 5).Not green locally, and not from this branch:
TestIntegrationWorktreeKnownLimitations(gitpacked-refs: Device or resource busy) andTestIntegrationWorkflowInterpretersRunnable(bwrap: execvp node, node at a non-standard path) fail ininternal/sandboxrun. Confirmed viagit stashthat both fail identically on a cleanmain.sandboxrundoes not importinternal/cli.Follow-up
clearActiveScope(internal/cli/cache.go:86-96) has a structurally similar scope switch, but it dispatches totoolcache.Clear*rather thanDescribe*; folding it in would mean changingtoolcache's public API, so it is deliberately left alone.--hash=cacheshould then expose both.🤖 Generated with Claude Code