Skip to content

verification: fix sig_for so a directory target can ever be Fresh - #154

Merged
alpibrupa merged 1 commit into
mainfrom
fix/verified-test-directory-sig
Sep 8, 2026
Merged

verification: fix sig_for so a directory target can ever be Fresh#154
alpibrupa merged 1 commit into
mainfrom
fix/verified-test-directory-sig

Conversation

@alpibrupa

Copy link
Copy Markdown
Contributor

Summary

  • verified.test/verified.spec_check records always name a directory (lex_test/lex_spec_check's own tool arg, e.g. "tests"), and sig_for used io.read unconditionally — which always errors on a directory — so harvest always stamped these records with sig: "". Since is_fresh treats a non-empty target with an empty sig as always Stale, VerifiedKindSeen("verified.test")/VerifiedTargetSeen(_, "verified.test") were permanently unsatisfiable for any task that asks for them.
  • Found live while building lex-economy's treasury module — the first task in that project ever to ask for verified.test (its earlier identity/contract/reputation modules only asked for verified.type_check).
  • Fix: sig_for now falls back to hashing the sorted concatenation of every file's path+content under a directory when a plain io.read fails, shelled through find ([proc]) rather than std.fs.walk ([fs_walk]) — sig_for is reachable from linter.lex's record_verified, which runs inside edit/write's Tool.execute, a row FIXED at [net, io, proc] by the Tool record type. Using proc instead reuses the exact trade record_verified's own comment already documents for mkdir vs std.fs.mkdir_p, and needed only 4 files widened instead of the 20+ a naive fs_walk widening cascades into.
  • Single-file targets are unaffected — io.read is tried first, so an existing sig's meaning doesn't shift for the common case.

Test plan

  • lex check on all 104 src/*.lex files
  • lex fmt --check src/ tests/
  • lex doc-sync --check
  • lex test --allow-effects crypto,fs_read,fs_write,io,random,sql,time tests (4 passed)
  • Live repro: sig_for on a real directory is stable across repeated calls, changes when a file under it changes, and matches the pre-fix plain-sha256 value for a single-file target (backward compatible)

🤖 Generated with Claude Code

verified.test/verified.spec_check records always name a directory
(lex_test/lex_spec_check's own tool arg, e.g. "tests" — never a single
file). sig_for used io.read unconditionally, which always errors on a
directory, so harvest always stamped these records with sig: "". Since
is_fresh treats a non-empty target with an empty sig as always Stale,
VerifiedKindSeen("verified.test") and VerifiedTargetSeen(_,
"verified.test") were permanently unsatisfiable for every project that
ever asks for them -- not flaky, 100% reproducible, for as long as the
tool's target stayed a directory.

Found live in lex-economy: the `treasury` task was the first task in
that project to ask for verified.test at all (identity/contract/
reputation only ever asked for verified.type_check), so this had never
been exercised before.

Fix: sig_for now falls back to hashing the sorted concatenation of
every file's path+content under a directory when a plain io.read
fails. Deliberately shells through `find` (`[proc]`) rather than
std.fs.walk (`[fs_walk]`): sig_for is called from linter.lex's
record_verified, which runs from inside edit/write's Tool.execute -- a
row FIXED by the Tool record type at [net, io, proc] (effect rows
unify by equality, not subtyping), so fs_walk is not an option there.
This reuses the same proc-instead-of-the-typed-effect trade
record_verified's own comment already documents for mkdir, rather than
inventing a second one -- and needed only 3 downstream signatures
widened (harvest, finish_turn, attest_verify_pass_if_clean; task_spec's
is_fresh/presence/presence_on/target_outcome/verified_outcome), not the
20+ files a naive fs_walk widening cascades into.

Single-file targets are unaffected: io.read is tried first, so an
existing sig's meaning does not shift for the common case.

lex check (104 files), lex fmt --check, lex doc-sync --check, and
lex test tests all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@alpibrupa
alpibrupa merged commit 657f8d3 into main Sep 8, 2026
1 check passed
@alpibrupa
alpibrupa deleted the fix/verified-test-directory-sig branch September 8, 2026 19:55
alpibrupa added a commit to alpibrusl/lex-economy that referenced this pull request Sep 8, 2026
test = ["tests"] used TestPasses, which runs `lex run <path> run_all` --
not this repo's actual `tests/test_*.lex` + `lex test` convention, so it
always failed. verified_on's per-file verified.test entry was also wrong:
lex_test's attestation always targets the whole "tests" directory, never
a single src file. Found both while diagnosing the first treasury build's
NOT SATISFIED result, which also surfaced a real upstream bug (a
directory-target verified.test record's sig was always computed as ""
and therefore always Stale) -- fixed at alpibrusl/lex-code#154 rather
than worked around here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
alpibrupa added a commit to alpibrusl/lex-economy that referenced this pull request Sep 8, 2026
Fourth module, first effectful one (identity/contract/reputation were
all pure). Storage + balance invariants only, per the task's own scope
note: balance_cents - committed_cents never goes negative, commit_funds
rejects insufficient_funds/currency_mismatch/invalid_amount cleanly with
no partial writes (wrapped in a BEGIN/COMMIT/ROLLBACK transaction),
settle_commitment can pay less than reserved but never more, and every
state change appends a lex-trail event (treasury.committed/released/
settled). lex-guard's Policy/SpendIntent gating is deliberately deferred
(see tasks/treasury.task's header) -- its merchant/category shape
doesn't map onto a company-to-company commitment yet.

Built via lex-code (opencode/kimi-k2.7-code, impl_test_fix_loop_verified)
after fixing two real bugs found along the way: the task spec's own
test/verified_on criteria were wrong (lex-economy#-, see the previous
commit), and lex-code's verified.test attestation for a directory target
was permanently unsatisfiable (alpibrusl/lex-code#154). Independently
re-verified: lex check, lex fmt --check, and lex test all pass; test
suite covers idempotent open, commit/release/settle round trips, the
insufficient_funds boundary, double-release rejection, overpay
rejection, and confirms no state changes on every error path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant