Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation #3

Open
toasterson wants to merge 8 commits from claude/wi-019e535b-three-tier-processing-gate-observe-sesha into main
Owner

Draft PR for Anima work item Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation (019e535b-10a2-7b73-972f-07c984fee9aa).

This PR is a scaffold opened by anima-triage. The implementation worker picks it up from here.

Close or mark ready manually once the implementation lands.

Draft PR for Anima work item **Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation** (`019e535b-10a2-7b73-972f-07c984fee9aa`). This PR is a scaffold opened by `anima-triage`. The implementation worker picks it up from here. - Marker: [`.anima/wip/019e535b.md`](.anima/wip/019e535b.md) - Current phase: `triaged` Close or mark ready manually once the implementation lands.
wip: triage Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation
Some checks failed
CI / test (pull_request) Failing after 2h0m0s
CI / test (push) Failing after 2h0m1s
CI / docker (pull_request) Has been skipped
CI / docker (push) Has been skipped
3cffa728b3
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 10:45:37 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 11:00:12 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 11:15:40 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 11:30:12 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 11:46:04 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 12:00:48 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 12:15:48 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 12:30:14 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 12:31:21 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 12:45:36 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 14:15:42 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-24 14:30:14 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 09:16:04 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 10:45:18 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 13:46:05 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 14:00:34 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 14:15:45 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 14:30:18 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 14:45:50 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 15:00:26 +00:00
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 15:15:37 +00:00
toasterson changed title from Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-05-25 15:30:32 +00:00
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 3cffa728b3
Some checks failed
CI / test (pull_request) Failing after 2h0m0s
CI / test (push) Failing after 2h0m1s
CI / docker (pull_request) Has been skipped
CI / docker (push) Has been skipped
to 14d10d331e
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-05-25 19:15:29 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 14d10d331e
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to db95e37be7
Some checks failed
CI / test (push) Failing after 2h0m32s
CI / test (pull_request) Failing after 2h1m40s
CI / docker (push) Has been skipped
CI / docker (pull_request) Has been skipped
2026-05-25 19:45:41 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from db95e37be7
Some checks failed
CI / test (push) Failing after 2h0m32s
CI / test (pull_request) Failing after 2h1m40s
CI / docker (push) Has been skipped
CI / docker (pull_request) Has been skipped
to ef2e157655
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-05-31 20:46:18 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from ef2e157655
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 7113c9ebfb
Some checks failed
CI / test (push) Failing after 1m19s
CI / test (pull_request) Failing after 1m12s
CI / docker (push) Has been skipped
CI / docker (pull_request) Has been skipped
2026-05-31 21:31:24 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 7113c9ebfb
Some checks failed
CI / test (push) Failing after 1m19s
CI / test (pull_request) Failing after 1m12s
CI / docker (push) Has been skipped
CI / docker (pull_request) Has been skipped
to 48f120a97b
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 01:33:20 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 48f120a97b
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 3cdce92d82
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 01:46:10 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 3cdce92d82
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 20c1995535
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 03:04:47 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 20c1995535
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to f296c92d07
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 03:15:47 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from f296c92d07
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 621791f827
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 03:45:48 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 621791f827
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to f9a7da11f2
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 05:32:11 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from f9a7da11f2
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to f1be81ff08
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 06:01:15 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from f1be81ff08
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 9efdd6540c
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 06:17:32 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 9efdd6540c
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 86d320b67a
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 06:32:17 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 86d320b67a
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to d28e8984a8
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 07:47:03 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from d28e8984a8
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 2ad6fa4cdc
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 08:33:20 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 2ad6fa4cdc
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 7beabeb7db
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 09:32:33 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 7beabeb7db
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 32a6e2e0cb
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 10:35:33 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 32a6e2e0cb
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to aa651fb14f
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 12:05:47 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from aa651fb14f
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to ede1c11af0
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 12:17:31 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from ede1c11af0
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to 65cfe03293
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 13:06:12 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from 65cfe03293
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to b41c58309d
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
2026-06-01 14:19:18 +00:00
Compare
toasterson force-pushed claude/wi-019e535b-three-tier-processing-gate-observe-sesha from b41c58309d
Some checks are pending
CI / test (push) Waiting to run
CI / docker (push) Blocked by required conditions
CI / test (pull_request) Waiting to run
CI / docker (pull_request) Blocked by required conditions
to c7bb801948
Some checks failed
CI / test (push) Failing after 2h0m1s
CI / test (pull_request) Failing after 2h0m1s
CI / docker (push) Has been skipped
CI / docker (pull_request) Has been skipped
2026-06-01 15:20:18 +00:00
Compare
Native CI: add .solstice/job.sh and share one check script with Forgejo
Some checks failed
CI / docker (push) Has been cancelled
CI / docker (pull_request) Has been cancelled
CI / test (push) Has been cancelled
CI / test (pull_request) Has been cancelled
e5ead22bbf
`get_project_ci_status` reported `resolvable: false` for Sia — no
`default_ci_steps` and no `.solstice/job.sh` — so every native dispatch
failed at config resolution before running anything.

That failure is not neutral. anima-server's solstice.rs routes it to
`handle_config_failure`, which parks the work item with
`ParkedReason::CiConfigUnresolvable`. The ABSENCE of this file reads as
"this work item's CI is broken" and parks real work. akh-medu learned this
the expensive way (8/8 native dispatches failed, the WI 1/2/3 series parked)
and its job.sh states the rule: a missing file must not read as "the code is
red". Sia had CI only on the Forgejo-Actions lane, so the native lane — the
one Anima's exact-head gate reads — had never produced a verdict at all.

One definition of the checks, two lanes
---------------------------------------
scripts/ci-checks.sh is now the only place the cargo commands are written.
Both .solstice/job.sh and .forgejo/workflows/ci.yml call it; neither re-lists
them. akh-medu's rule is that a native verdict must never be red for work the
Actions lane would call green, because the disagreement parks work items on
phantom failures. Two inline copies drift the first time someone edits one;
sharing the script makes that structurally impossible rather than a reviewer's
responsibility.

job.sh only provisions. It deliberately differs from the Anima/akh-medu
scripts in two places:

- It does not assume passwordless sudo. Those scripts call `sudo apt-get`
  unconditionally because their image grants it; a runner without sudo would
  fail with a bare "sudo: command not found" that classifies as a code
  failure and parks the item. Privilege is resolved once, only missing
  packages are installed, and the no-privilege case exits with a message
  naming the packages and the reason.
- It does not `rm -rf ../anima`. The workflow did, and on a persistent or
  shared runner that deletes a checkout the job does not own. An existing
  sibling is reused; a non-git path there is refused rather than removed.
  The resolved revision is always logged so an upstream anima-proto change
  is attributable. ANIMA_SIBLING_REV pins a run without freezing CI to a
  revision the shipped image does not use.

protoc keeps ci.yml's pinned 25.3 release, since Ubuntu's 3.12 rejects
anima-proto's proto3 `optional` fields, and installs into the checkout so it
needs no privileges. The build is debug: release codegen is what OOM-killed
rustc on the Solstice VMs for akh-medu, and an OOM reads as red on healthy
code. Sia's release build is still covered by the `docker` job on push to
main.

Validation
----------
scripts/test-ci-job.sh replaces every external command with a logging stub and
asserts on the control flow — the branches that only execute on a runner in an
unusual state, i.e. never until the day they matter. 10 scenarios, 36
assertions, all passing; it needs no toolchain and runs in seconds, so it is
wired into ci-checks.sh and guards the CI definition on both lanes.

It earned its keep immediately, catching three defects before any runner saw
the script: `"${profile_args[@]}"` on an empty array is an unbound-variable
error under `set -u` on bash < 4.4, so the script would have worked on the
runner but not locally, defeating the point of sharing it; the openssl probe
hardcoded /usr/include/openssl/ssl.h and reported a false "missing" on any
other layout, triggering a pointless privileged install; and the harness's own
no-sudo case was inheriting the host's cached sudo credentials, leaving the
no-privilege branch untested exactly where it was most likely to be wrong.

No application change: no Cargo.toml edit, no dependency version change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Native CI: fix four blockers found in review of e5ead22
Some checks failed
CI / test (pull_request) Failing after 5m45s
CI / docker (pull_request) Has been skipped
CI / test (push) Failing after 7m1s
CI / docker (push) Has been skipped
255b3fc3b2
All four were real. Reproduced each before fixing.

1. The install died as root. `$SUDO DEBIAN_FRONTEND=noninteractive apt-get`
   looks like an assignment prefix but is not one: bash decides that from the
   text BEFORE expansion, and the first word is `$SUDO`. Once that expands to
   nothing — i.e. running as root, the case that should always work — the
   assignment becomes the command name:

       $ SUDO=""; $SUDO DEMO_ONLY=1 true
       bash: DEMO_ONLY=1: command not found   (rc=127)

   Routed through `env`, which is a real command in both shapes. The harness
   now exercises root explicitly via an `id` stub rather than only covering
   sudo/non-root, so the escalation-free path has real coverage.

2. A git worktree's `.git` is a FILE holding a gitdir pointer, so `[ -d .git ]`
   rejected a perfectly valid shared worktree — and the next branch then
   refused to run at all. This checkout is itself a worktree, so the script
   would have rejected the very tree it was written in. Now asks git
   (`rev-parse --is-inside-work-tree`) and confirms the path is that
   worktree's ROOT by comparing realpaths, so a subdirectory of an unrelated
   repo above it is still refused — non-destructively.

3. ANIMA_SIBLING_REV claimed a pin it never enforced: it was only applied on
   the clone path, so with an existing checkout the requested revision was
   silently ignored and the verdict was attributed to a revision that was
   never built. Now validated against the existing checkout and REFUSED on
   mismatch, without touching a checkout this job does not own. An unknown rev
   is likewise refused rather than fetched into it.

   ANIMA_SIBLING_DIR is gone. It was misleading: cargo reads the override path
   from the workspace [patch] table, so pointing the variable elsewhere
   provisioned a directory cargo would never look at while the build still
   failed on the real path. The sibling location is now derived from
   Cargo.toml, which is the only thing that actually decides it, and a missing
   [patch] path fails loudly instead of guessing.

4. `clippy --workspace --all-targets -- -D warnings` was stricter than the
   ci.yml it replaced (libs/bins only). Pulling tests, benches and examples
   into a -D warnings gate for the first time risks an unrelated baseline
   failure landing as a red verdict on unrelated work — the phantom-failure
   park this work item exists to stop. Restored to the previously-verified
   scope. Widening is a separate change that needs a clean baseline run and
   then moves both lanes together.

Also fixes unbounded recursion introduced when the harness was wired into
ci-checks.sh: the harness runs job.sh, which execs ci-checks.sh, which ran the
harness. Guarded with CI_SKIP_JOB_HARNESS, which the harness sets for its
children, and the guard itself is now covered.

Harness: 18 scenarios, 62 assertions, all passing. New fixtures are real, not
mocked around — an actual shared worktree created with `git worktree add`, an
actual non-root checkout, actual commit SHAs for the pin cases, and a real
Cargo.toml with a [patch] table so the derivation runs the same code path the
runner takes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Most of this work item already landed. main's decide_inbox consults
ctx.route for List, Service and Personal and returns
LeaveAlone{NotConfigured} when none matches, so "zero cost until a project
route is explicitly configured" holds for those kinds; route-pinned work
items already gate thread posts (file_into_route matches on
route.work_item); and every routed path already sends a derived summary to
Seshat rather than the body. Two rules were still broken.

Travel bypassed the gate. decide_inbox dispatched HeaderKind::Travel
without consulting ctx.route at all — the one inbox kind that did — and
decide_travel then unconditionally emitted AnimaEnsureProject{"Travel"} plus
SeshatIngest. On a deployment with zero routes configured a booking
confirmation was therefore summarized, ingested, and had a project created
for it. This is not hypothetical: the Anima project list carries a
"Travel / Auto-curated travel itinerary inbox" project that nobody
registered by hand. Travel is now gated like every other kind. The existing
test asserted the bypass as intent ("Travel routes regardless of
sender_routes config"); it is replaced by a pair covering both sides, so the
feature still works once a route exists.

The owner-sent path put the raw body in Seshat. decide_owner_sent computed
content = readable_body(...) and passed it to BOTH the Anima description and
SeshatIngest.body, making it the single leak of "raw mail bodies are never
stored in Seshat. Only summaries." It now derives a summary the same way the
other paths do and sends that, while content still reaches the Anima
description — the acceptance rules restrict Seshat, not Anima, and the body
stays useful where a human reads it. The new verdict_summary helper reuses
the existing per-variant summarizers rather than adding a sixth
near-duplicate, because an owner reply can carry any body shape.

Deliberately NOT implemented, because main has moved past the brief:

  - "sia never creates work items" is superseded. main emits
    AnimaReconcileWorkItem for list mail under a documented rationale
    ("this makes discussions visible in Anima ... rather than hidden"),
    backed by newer infrastructure (work_item_links, migration
    m20260714_000001) and an open WI-12 about reconcile_work_item's
    draft-gate provenance guard. Removing it would revert a later product
    decision and break active work.
  - The structured escalation filters (header_kind / list_id /
    sender_pattern / subject_contains) were replaced by the per-route
    natural-language filter from WI-9, which is done and merged.

Both are reported rather than "fixed".

Tests: 12 in the engine suite (4 new), 25 in the lib, all passing. clippy
-p sia-mail clean at the ci.yml scope, cargo fmt --all --check clean,
cargo check --workspace clean. No production mail was touched; every
assertion is over a constructed ParsedContext.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Puts PR #3's original head into this branch's ancestry so the canonical
work-item branch can fast-forward without a force push and without losing
the PR's history.

The final tree is deliberately the reconciled one, not a textual union.
PR #3 is 44 commits behind main and conflicts in 12 files: most of what it
proposed has since landed on main by other routes (the route gate for
List/Service/Personal, route-pinned thread posts, summary-only Seshat
ingestion on the routed paths), and two of its acceptance rules were
superseded by later product decisions — AnimaReconcileWorkItem for list
mail, and WI-9's natural-language route filter replacing the structured
escalation filters. Merging its content would resurrect the pre-split
pipeline and revert both.

What remained genuinely unmet is implemented here against main's current
shape: the Travel gate bypass and the raw body reaching Seshat from the
owner-sent path.

Net diff against current main is unchanged by this merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge the native CI job script so this head can produce a CI verdict
Some checks failed
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / test (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
60ee2f217d
Native dispatch reads .solstice/job.sh from the tested commit; without it
every dispatch against this head fails at config resolution and re-parks the
work item. Additive; no application code on this branch is modified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
toasterson changed title from WIP: Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation to Three-tier processing gate: Observe (Seshat) → Classify → Act with manual WI creation 2026-09-22 11:02:18 +00:00
CI: make the job-script harness hermetic in command visibility and identity
All checks were successful
CI / test (push) Successful in 31m30s
CI / docker (push) Has been skipped
CI / test (pull_request) Successful in 29m37s
CI / docker (pull_request) Has been skipped
91070144c8
Native Linux CI on 255b3fc failed 56 passed / 6 failed
(request 982fb0cb-eb22-46d6-8dc1-2076e0e4e19c). The harness was not hermetic,
so it asserted against the host instead of the fixture, and the same suite
passed on macOS and failed on the CI container.

Two leaks, both real.

PATH fell through to /usr/bin:/bin. "Missing pkg-config" was therefore not
missing on any image that ships it, so scenarios 4, 5 and 11 silently
exercised the already-provisioned path rather than the install branches they
name. macOS has no /usr/bin/pkg-config, Debian does — hence the split.

Identity was inherited. job.sh branches on `id -u`, and an `id` stub was
installed only for the root scenario. On a container running as root every
"non-root" scenario took the root path, leaving the no-privilege branch
untested exactly where it was most likely to be wrong.

Fixes, in the harness only — job.sh's provisioning semantics are unchanged:

  - $BIN is now the ENTIRE PATH under test. Every legitimately-needed real
    tool is linked into it explicitly (bash sh env git sed awk grep cut head
    sort uniq mkdir rm cp ln chmod cat true false dirname basename touch wc),
    and nothing else is reachable, so absence is real absence.
  - `id` is stubbed for EVERY mode from the scenario's declared identity
    (root -> 0, otherwise 1000), never inherited from the host.

No assertion was dropped or weakened; the six that were failing now exercise
what their names claim. A new scenario 19 guards the leak itself — that PATH
under test is exactly the sandbox bin, that an unprovided command is absent,
that host pkg-config is unreachable, that identity is the declared one, and
that real tools are still reachable — so this cannot regress silently into
"passes on the developer's machine".

Proof, same container both ways (debian:bookworm-slim, pkg-config installed):

  pre-fix  (255b3fc harness), uid 0 : passed: 56   failed: 6
                                      exactly the six CI reported
  post-fix (this commit),     uid 0 : passed: 68   failed: 0
  post-fix (this commit),  uid 1000 : passed: 68   failed: 0
  post-fix, macOS host              : passed: 68   failed: 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge the hermetic-harness CI fix
Some checks failed
CI / test (push) Successful in 28m57s
CI / docker (push) Has been skipped
CI / test (pull_request) Failing after 33m26s
CI / docker (pull_request) Has been skipped
06b30d491c
Native Linux CI on the CI branch failed 56/6 because the job-script harness
was not hermetic: PATH fell through to /usr/bin so "missing pkg-config" was
not missing on Debian, and `id` was inherited so non-root scenarios ran as
root. Both are fixed in the harness only; job.sh's provisioning semantics are
unchanged.

Additive: no application or packaging change on this branch is modified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Some checks failed
CI / test (push) Successful in 28m57s
CI / docker (push) Has been skipped
CI / test (pull_request) Failing after 33m26s
CI / docker (pull_request) Has been skipped
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin claude/wi-019e535b-three-tier-processing-gate-observe-sesha:claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git switch claude/wi-019e535b-three-tier-processing-gate-observe-sesha

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git switch claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git rebase main
git switch main
git merge --ff-only claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git switch claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git rebase main
git switch main
git merge --no-ff claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git switch main
git merge --squash claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git switch main
git merge --ff-only claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git switch main
git merge claude/wi-019e535b-three-tier-processing-gate-observe-sesha
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
toasterson/sia!3
No description provided.