Skip to content

096-01: orientation reports work in flight (open PRs + unmerged branches)#122

Open
Kyarha wants to merge 2 commits into
mainfrom
claude/orient-sees-work-in-flight
Open

096-01: orientation reports work in flight (open PRs + unmerged branches)#122
Kyarha wants to merge 2 commits into
mainfrom
claude/orient-sees-work-in-flight

Conversation

@Kyarha

@Kyarha Kyarha commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

The problem

Both orientation layers — the deterministic workflow.py orient headline (088-01) and the /jig:orient judgment briefing (088-02) — read only the project's own files. Neither looks at the collaboration layer.

A status board describes the default branch. Work that is finished but parked — in an open PR awaiting the owner, or on a branch nobody merged — is invisible to every artifact orientation reads, so it reports the project as unblocked.

The sharper half is what unattended workers do: a night/cron worker that finishes a run opens a PR and writes its questions for the owner in the PR description — not into any file in the repo. Orientation never opens that description.

This is a field report, not a hypothesis. In a downstream project an overnight worker left a PR titled "(5 decisions need you)" whose body posed five questions in plain language with recommendations. A "what's next" pass surveyed specs, ADRs, refinement-todo, the inbox, the bug board, commits and branches — every local source — and never ran gh pr list. It found the same work as an unmerged branch, concluded it was uncommitted drafts, re-derived the questions from the review files, got four of five, missed the fifth entirely, and put worse-framed versions to the owner, who had to answer them twice. Recorded in the spec's Overview.

What this changes

Deterministicworkflow.py orient gains one optional trailing segment:

jig hint: Scaffolded jig project · active specs: 002-004 DRAFT · focus: 002-01 DRAFT · in flight: 3 commits ahead of main on claude/night-prep

Absent — and the headline byte-identical to its pre-096 form — in every degraded case: not a git repo, no git on PATH, no resolvable trunk, detached HEAD, level with or behind trunk, or any git failure.

Judgmentskills/orient/SKILL.md gains open PRs as its first survey item (gh pr list + gh pr view to read the body), a "Waiting on you" output section, a cross-check against re-deriving decisions, and the generalising rule in Judgment.

Design notes worth reviewing

  • The bound is aggregate, not per-call. _in_flight_summary can issue nine git calls. A per-call timeout × 9 blows the SessionStart hook's 4 s ceiling, and the hook swallows a timeout as no orientation at all — strictly worse than before this slice. All calls share one 1.5 s wall-clock deadline; once spent, the rest return without spawning.
  • origin/HEAD is verified, not trusted. A stale one (default-branch rename, pruned target) would otherwise silence the segment permanently, or produce a confidently wrong count against an old trunk.
  • Ref names are sanitised before reaching the injected headline. Git permits · — the headline's own field separator — in a ref name, so an unsanitised branch could forge a field. Reuses the existing claim whitelist at a cap that fits real branch names.
  • No gh in the CLI. It needs network and auth; the PR survey lives in the judgment layer only.

Review record

Three independent passes, each of which found real defects — all applied:

Pass Verdict Found
compliance r1 needs-changes per-call timeout wasn't a bound (~16 s worst case); three tests passed with the feature deleted
compliance r2 needs-changes UnicodeDecodeError escaped orient() (a ValueError, uncaught); ref names unsanitised into the injected headline — AC9 added rather than fixed silently
reconciliation needs-changes a fourth vacuous test (< 400 chars passed with the cap deleted); four places where the written record disagreed with the tree

The deviation log carries 12 entries, including the ones nobody asked for: the per-call timeout dropping 2.0 s → 0.75 s can make the segment vanish on a cold repo (AC2-compliant, but a real behaviour change), AC4's origin/<trunk> phrasing not literally describing the code, and the first-resolvable-candidate heuristic reporting against main in a develop-trunk repo with a stale local main.

Honest limitation, stated in the log: all three reviewer subagents had read-only tools, so every executable claim was verified by the implementer. The independent checks are of the code and the record, not of the test run.

Verification

  • python3 scripts/run_tests.pyRan 3527 tests … OK (skipped=4), pyright: clean
  • python3 scripts/build_host_packages.py --check → in sync
  • python3 scripts/spec_lint.py → no contradictions across 95 specs
  • The pinned pre-096 headline test (test_scaffolded_headline_compacts_active_specs) is provably unedited — zero deletion lines in its diff
  • The aggregate-budget test is mutation-checked: disabling the shared deadline makes it fail (2.69 s vs a 2.0 s assertion)

🤖 Generated with Claude Code

Kyarha and others added 2 commits July 22, 2026 21:11
Both orientation layers read only the project's own files, so work that
is *finished* but parked — in an open PR, or on a branch nobody merged —
is invisible to every artifact they consult, and orientation confidently
reports the project as unblocked.

The sharper half: unattended workers put their questions for the owner in
the **PR description**, not in any file. So an agent asked "what's next"
never sees them, re-derives the question set from raw review artifacts,
and puts a worse-framed subset to the owner as if it were new. That
happened in a downstream project (spec 096 Overview records it): four of
five questions re-derived, the fifth missed entirely.

Deterministic layer — `workflow.py orient` gains one optional segment:

  · in flight: 3 commits ahead of main on claude/night-prep

Silent in every degraded case, so the pre-096 headline stays byte-
identical: not a git repo, no git binary, no resolvable trunk, detached
HEAD, level with or behind trunk, or any git failure. All git work shares
one 1.5 s wall-clock deadline — a per-call timeout is not a bound when
nine calls are possible, and overrunning the SessionStart hook's 4 s
ceiling would leave the session with *no* headline at all, strictly worse
than before. `origin/HEAD` is verified like any other candidate rather
than trusted, so a stale one cannot silence the segment or produce a
count against the wrong trunk. Both ref names are sanitised and capped
before reaching the injected headline — git permits `·`, the headline's
own field separator, in a ref name.

Judgment layer — `skills/orient/SKILL.md` gains open PRs as its first
survey item (`gh pr list` + `gh pr view` to read the *body*), a "Waiting
on you" output section flagging stale / unmerged-no-PR / superseded, a
cross-check so a question already put to the owner is not re-derived, and
the generalising rule: "what's next" means what is blocked on a human,
not what the repo contains.

Three review passes, all of which found real defects:
- compliance r1: the per-call timeout was not a bound; three tests passed
  with the feature deleted
- compliance r2: UnicodeDecodeError escaped `orient()`; ref names reached
  the injected headline unsanitised (AC9 added rather than fixed silently)
- reconciliation: a fourth vacuous test, plus four places where the
  written record disagreed with the tree

All findings applied. 3527 tests, pyright clean, host packages in sync.
The pinned pre-096 headline test is provably unedited — zero deletion
lines in its diff.

Co-Authored-By: Claude Opus 4.8 <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