Skip to content

docs(skills): batch-lifecycle carries the closed-job write gate (cf. #439) - #450

Merged
JArmandoAnaya merged 1 commit into
mainfrom
docs/batch-lifecycle-closed-job
Aug 8, 2026
Merged

docs(skills): batch-lifecycle carries the closed-job write gate (cf. #439)#450
JArmandoAnaya merged 1 commit into
mainfrom
docs/batch-lifecycle-closed-job

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

What changed

Governance only — no product code, no wire contract, no behaviour. #447 shipped the closed-job
write gate; the documents that a future session reasons from still described the world before it.

The skill (.agents/skills/domain/batch-lifecycle/SKILL.md)

  • OPEN_JOB_STATES = {pending, in_progress} joins the derived sets, and JOB_TRANSITIONS now
    says out loud that completed has no exit either.
  • Decision 2 was "annotation writes are gated on progress" and is now "on progress and on
    the job": WRITABLE_PROGRESSAssetNotWritable (409), OPEN_JOB_STATESJobFinished
    (409 JOB_FINISHED), with JobService.mark reading the same set. It names OPEN_JOB_STATES
    as the single source that the declaration (asset_actions, three dimensions, none optional)
    and the refusal (JobService.require_open_job) both read, so the two cannot disagree — and it
    states why the batch gate never covered this: JobService.complete does not complete the
    batch, so a finished job ordinarily sits inside one that is still in_annotation.
  • Decision 9 records the two rulings fix(annotator): completing a job flips the workspace to read-only in place #447 shipped that read as untidiness and are not:
    frame-verb gating is job-level rather than frame-level (a skipped frame is read-only
    per-frame and still needs its Un-skip; the nav cluster is measured to one width — cf. Completed batch opens editable: edit functions remain active in the read-only workspace #423,
    Annotation top bar: consolidate image navigation into a centered cluster #416), and complete is the job's declaration rather than the last frame's, so Finish job
    stays reachable on a settled last frame.

The false-sentence sweep

Everything below claimed one or two lifecycle gates where there are now three. Each is prose;
none changes a rendered string or a wire field.

File The sentence it corrected
.agents/skills/frontend/ui-capabilities/SKILL.md read-only named only "annotator on a non-in_annotation batch", and described the mode purely as an entry state — it is now also a transition a mid-session Finish job performs
docs/annotations.md "Work only happens inside an open batch" / "the same two lookups (require_job, require_open_batch)"; and "The batch gate is the guard instead" under delete has no confirm=
docs/jobs.md "Per asset, inside an in_annotation batch"; "annotate … is WRITABLE_PROGRESS and the batch gate together"
docs/ui.md "the kernel derives it from both dimensions … One question, both causes" — plus the read-only section had no account of the in-place transition at all
docs/mcp.md "a completed one is left alone" (it is now refused by the write's own gate); "writing is gated on the batch" in the #109 measurement
docs/mcp-walkthrough.md #109's entry: "given that writes are gated on the batch" — kept as the historical framing it is, with the job gate noted as not reopening the question
src/visionset/mcp/_autostart.py module docstring: "writing is gated on the batchand never on the job"
frontend/ui-core/src/annotator/AnnotationPage.tsx the readOnly derivation comment: "from both dimensions"
frontend/ui-core/src/screens/GalleryScreen.tsx two comments (the gallery's editable door and the selection bar's) saying the kernel derives declarations "from both dimensions"

The MCP test docstring that had written the hole down as a rule — "Writing here is legal — the
gate is the batch"
— was already corrected in #447 and is quoted in the skill as the worked
example.

Found, not fixed

src/visionset/server/routes/annotations.py:60"Reading is not gated on the batch being open:
a label outlives the work that produced it."
True and now incomplete: reads pass no job gate
either. It is a route description, so it is published verbatim into openapi.json and
frontend/ui-core/src/generated/api.ts — a wire contract, not prose. Left alone deliberately
rather than pulled into a docs PR; the sentence is not wrong, only narrower than the rule.

Test plan

Prose and comments only, so nothing new is asserted. The gate is the check.

bash scripts/check.sh was run in stages (the ~10-minute command ceiling), exit code per stage:

pytest tests/architecture        EXIT=0
pytest tests/cli                 EXIT=0
pytest tests/examples            EXIT=0
pytest tests/formats             EXIT=0
pytest tests/inference           EXIT=0
pytest tests/jobs                EXIT=0
pytest tests/kernel              EXIT=1   <- baseline, see below
pytest tests/mcp                 EXIT=0
pytest tests/packaging           EXIT=0
pytest tests/scripts             EXIT=5   <- no pytest tests collected; that directory is
                                             node --test, covered by `check.sh generated`
pytest tests/server              EXIT=0
pytest tests/test_versioning.py  EXIT=0
ruff check .                     EXIT=0
ruff format --check .            EXIT=0
mypy src/visionset               EXIT=0
lint-imports                     EXIT=0   (4 contracts kept)
pnpm -r build                    EXIT=0
pnpm test                        EXIT=0   (868 annotator + 742 ui-core)
pnpm -r lint                     EXIT=0
bash scripts/check.sh generated  EXIT=0   (four drift gates)
bash scripts/check.sh browser    EXIT=0   (e2e chromium 231 passed; cycle chromium 1 passed)

The first pass of the frontend and browser stages failed for an environment reason and is
recorded rather than hidden: a fresh worktree has no node_modules, so pnpm -r build died on
sh: 1: tsc: not found and the three stages behind it never ran. pnpm install --frozen-lockfile
and a re-run produced the codes above. ruff check . also failed once, on an E501 this PR
introduced (_autostart.py:9, 103 > 100); the docstring was rewrapped and re-run clean.

Merged under the baseline-proof exception, step python tests, cf. #444

tests/kernel is red for two tests that have nothing to do with this diff.

On this branch (56da20f):

FAILED tests/kernel/test_ingest_service.py::test_a_truncated_clip_keeps_what_decoded_and_reports_the_break
FAILED tests/kernel/test_video_processor.py::test_a_truncated_clip_yields_what_decoded_and_then_refuses
>       assert 0 < result.created < clip.frame_count
E       AssertionError: assert 0 < 0
tests/kernel/test_ingest_service.py:498: AssertionError

On unmodified main at the merge-base 856d2f8, clean tree, this machine, run by me now:

$ git rev-parse HEAD            -> 856d2f85c5bff15fa89408fae709488c459ba186
$ git status --porcelain        -> (empty)
$ uv run pytest -q tests/kernel/test_ingest_service.py::test_a_truncated_clip_keeps_what_decoded_and_reports_the_break \
                   tests/kernel/test_video_processor.py::test_a_truncated_clip_yields_what_decoded_and_then_refuses
FAILED tests/kernel/test_ingest_service.py::test_a_truncated_clip_keeps_what_decoded_and_reports_the_break
FAILED tests/kernel/test_video_processor.py::test_a_truncated_clip_yields_what_decoded_and_then_refuses
BASELINE EXIT=1

Identical failure, identical assertion. The diff does not touch that surface: it changes
Markdown, two TSX comments and one Python module docstring — nothing in
kernel/adapters/ffmpeg_video_processor.py, kernel/services/ingest_service.py, their tests, or
any ffmpeg invocation. What the failure exercises is video decoding of a deliberately corrupt
clip. The tracking issue is #444 (open); it is core-count dependent, not an ffmpeg-version
problem, and CI's python job installs the same package and passes.

cf. #439, cf. #447, cf. #444 — no closing keyword anywhere in this PR, deliberately: it resolves
no issue.

…439)

The skill encoded the pre-#447 world: annotation writes gated on asset
progress alone, with no job dimension. Left as it was, the next design
session consulting it re-derives the hole that #439 closed.

Decision 2 now states both dimensions and names OPEN_JOB_STATES as the one
set the declaration (asset_actions) and the refusal (require_open_job) both
read, so they cannot disagree; it also states why the batch gate never
covered this — a job completing does not complete its batch. Decision 9
records the two rulings #447 shipped and a later reader would tidy away:
frame-verb gating is job-level, and Finish job stays reachable on a settled
last frame.

Prose sweep for the same false sentence: docs/annotations.md, docs/jobs.md,
docs/ui.md, docs/mcp.md, docs/mcp-walkthrough.md, mcp/_autostart.py,
AnnotationPage.tsx and GalleryScreen.tsx all described one or two gates
where there are now three. ui-capabilities gains the read-only transition,
which is what a mid-session Finish now performs.
@JArmandoAnaya
JArmandoAnaya merged commit 714d18f into main Aug 8, 2026
13 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the docs/batch-lifecycle-closed-job branch August 8, 2026 14:48
JArmandoAnaya added a commit that referenced this pull request Aug 10, 2026
…te (cf. #450) (#521)

* docs(api): the annotations read is not gated on job or batch state

The route description said reading is not gated on the batch being open. That is
true but narrower than the rule the service enforces: neither job state nor batch
state gates a read. The description is a published wire string, so openapi.json and
the generated client are regenerated in the same change.

* docs: the branch and merge affordances are settled, not deferred

DESIGN.md's Provenance section recorded the version-control affordances as
post-beta and pending a decision, and three passages promised they return with the
model behind them. Neither half holds: the top-bar slots were removed on 2026-08-05,
and the branch-and-merge model was settled on 2026-08-10 as superseded by the batch,
review and release model the product already has.
JArmandoAnaya added a commit that referenced this pull request Aug 21, 2026
…te (cf. #450) (#521)

* docs(api): the annotations read is not gated on job or batch state

The route description said reading is not gated on the batch being open. That is
true but narrower than the rule the service enforces: neither job state nor batch
state gates a read. The description is a published wire string, so openapi.json and
the generated client are regenerated in the same change.

* docs: the branch and merge affordances are settled, not deferred

DESIGN.md's Provenance section recorded the version-control affordances as
post-beta and pending a decision, and three passages promised they return with the
model behind them. Neither half holds: the top-bar slots were removed on 2026-08-05,
and the branch-and-merge model was settled on 2026-08-10 as superseded by the batch,
review and release model the product already has.
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