Skip to content

ci: verify feat/jev-policy on GitHub runners - #1

Closed
aneym wants to merge 15 commits into
mainfrom
feat/jev-policy
Closed

aneym wants to merge 15 commits into
mainfrom
feat/jev-policy

Conversation

@aneym

@aneym aneym commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Fork-internal PR opened only to run the repository's CI workflows on this exact commit, because the upstream PR (callstack#2654) is gated on maintainer approval. Not for merge.

alexneyman and others added 15 commits September 16, 2026 18:06
`--policy`, `--min-confidence`, and `--input` for the policy commands, and one
consumer added to the existing `--max-steps` declaration rather than a second
flag under the same name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The neutral half of the policy feature: the candidate projection a policy head
chooses between, a content digest that detects an action which changed nothing,
and the caller-supplied text map a field is filled from.

The digest excludes refs on purpose. Refs are reissued per snapshot generation,
so including them would make every screen look changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One provider behind a named abstraction, over global fetch with no new
dependency. The credential comes from TYPESAFE_API_KEY in the environment only,
so it never reaches argv, a recorded script, or an MCP tool schema.

Every field of a response is validated against the candidate set rather than
trusted, and an unreachable head fails with the status as a typed reason plus
the documented fallback: the agent's own snapshot-and-choose loop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Snapshot, decide, act, verify, repeat. Three device operations behind a narrow
port so the loop is testable without a device.

What the loop had to learn from a real iOS sign-in flow:

- A settled observation only proves the local UI went quiet, so a transition
  waiting on the network still reads as the old screen. One grace re-read
  separates a slow transition from an action that did nothing.
- A masked field reformats what it receives, so the runner reports
  TEXT_ENTRY_MISMATCH on text that arrived. The screen decides, not the write.
- A digit-box code field never reflects its value at all. A confirmed value is
  sufficient evidence of a write but not necessary; a screen that moved on is
  the other half, and the on-screen keypad is the fallback when neither holds.
- A press on a text field only focuses it, so the supplied entry rather than the
  field's emptiness decides whether to write.
- A head asked whether progress is blocked says yes on any sign-in screen. A
  concrete target at 0.9 confidence outranks that flag, or the loop stalls on
  step one of every sign-in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`suggest <goal>` prints one typed decision without acting. `act <goal>` runs the
loop. Both are local-cli commands: they compose snapshot, press, and fill from
the client process and own no daemon route, which is what keeps them out of the
public catalog that requires one.

Every nested call is an ordinary daemon command, so a policy-driven run carries
the same claims, ref frames, and recording behaviour as a run an agent drives by
hand, and each mutation is pinned to the generation of the snapshot that issued
its ref.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both are pure delegators, so they join the `none` device-claim list. `act` joins
the daemon-preserving and unbounded-envelope lists: a run is a sequence of
ordinary commands that each carry their own envelope, and an outer one would
abort a run still making progress while a reset would destroy the session the
remaining steps need.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A command-reference section, a reference page covering the decision shape, the
text-supply rule, the post-action checks, and the limitations, plus one
paragraph on the skill routing card.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The changed-code quality gate flagged the step loop as critically complex and
six exports as unconsumed. The loop now reads as snapshot, decide, resolve step,
record, with the write and press branches and the keypad walk as their own
functions, and the candidate projection resolves a node's role separately from
building the candidate. The internal constants are module-local.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The hint read "Without it, Fall back to agent-driven policy" wherever it was
interpolated. The clause now lives in its own module, lower-cased, so both the
provider and the composer can name it without one importing the other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A policy asked whether progress is blocked says yes on anything that looks like
a wall. In live runs on an iOS sign-in flow the flag was set twice while the
policy also named the element that led onward: the phone field on the sign-in
screen, and "I'll start fresh" on an onboarding page offering a file import. The
previous rule acted through the flag only above 0.9 confidence, so the second
one ended the run three screens short of the goal.

The flag is now terminal only together with nothing worth acting on: no target,
or one below --min-confidence. A named target above the floor is acted on. Being
wrong there costs one step that the same-screen check catches and the step budget
bounds; believing the flag costs the run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The CLI startup closure must not grow, and a static edge from the client facade
pulled the whole policy subtree into every invocation. The policy head is
optional, so its client loads inside the two methods that use it, the way debug
symbols already does.

Also replaces a `as string` on the chosen target with a discriminated judgment,
and reaches the client fixture's device group through the spread idiom that file
already uses for observability, so the oversized-test-file ratchet holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ady reaches

The eager-closure gate holds src/cli.ts to the module count at the merge-base,
and a command family is reached from it through the family registry, so a new
family directory grows it by every module the family index evaluates. The gate
names the remedy: give the new code a home in a module the closure already
evaluates.

suggest and act join the interaction family, whose vocabulary they already
share: find, get, and is are reads that name an element, and act is a loop over
the verbs beside it. Their CLI defaults move next to the flags that publish
them, so the help prose and the loop read one declaration. The policy runtime
stays under commands/policy and is still reached only through the client's
on-demand import.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The move left both descriptors naming a deleted module, which the explain-command
table caught, and left three exports with no consumers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nges

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The changed-line coverage gate passed at 83%, but the two largest gaps were the
seam that pins a mutation to its snapshot generation and the text both commands
print. A run that loses the pin fails silently, on whichever element inherited
the ref, so it is worth its own test rather than coverage by a live run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aneym

aneym commented Sep 16, 2026

Copy link
Copy Markdown
Owner Author

Closing: this fork has workflows disabled pending the one-time manual enable, so no CI can run here. Verification is recorded on callstack#2654.

@aneym aneym closed this Sep 16, 2026
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