Skip to content

Post-merge follow-up to #339: two protocol rules, and the two policies it left open - #340

Merged
JArmandoAnaya merged 2 commits into
mainfrom
docs/339-followup
Aug 5, 2026
Merged

Post-merge follow-up to #339: two protocol rules, and the two policies it left open#340
JArmandoAnaya merged 2 commits into
mainfrom
docs/339-followup

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

Post-merge follow-up to #339. Docs, one skill file, and the CHANGELOG — no source changes.

What changed

refactor-protocol gains two rules, both of which #339 paid for.

  • Whole-tree lint after a rebase. A rename is a whole-tree fact. An embedded job system: a JobQueue port, a SQLite queue, and a spawn dispatcher (#328) #339 renamed a test double;
    api: batch membership editing is not on the wire, and the gallery is now the caller #29 was waiting for #281 landed mid-flight with a fixture annotated on the old name; git merged both without a
    conflict because they are different lines, and every targeted pytest module passed, because an
    annotation is never evaluated. CI answered F821. So: after a rebase that brings in commits
    you did not write, ruff check . over everything.
  • A declared fallback for the local gate. A green bash scripts/check.sh is still what a merge
    requires. When another session demonstrably owns the machine — with evidence — the substitute
    is every static gate, the full frontend build and suite, and every affected pytest module, with
    green CI on clean runners as arbiter. It must be declared in the PR body before merging; a
    merge that lets a reader infer a local run that never happened is a protocol violation rather
    than a shortcut.

The two open review items on #339 are closed as decisions, written where the next person will
look rather than left as questions somebody re-opens:

  • exports/ has no retention policy, and that is the policydocs/releases.md (where export
    behaviour lives) and docs/workspaces.md (where the layout does). No TTL, no size cap, no
    sweeper, no DELETE route. The disk is the user's; a local-first tool deleting an exported
    training set on a schedule nobody chose takes an action nobody can undo. Same posture blobs and
    staged uploads already have. What it costs is stated — a workspace only grows — and so is the
    remedy, rm, which is safe for uploads/ and exports/ because both are reproducible and is
    emphatically not safe for blobs/.
  • The job table has no project_id, and no foreign key at alldocs/background-jobs.md,
    in "The decisions, and why" plus a one-line pointer in "Limits, stated". Scoping lives in the
    payload, because different job types are about different things and the column would be null
    for some and wrong for others. The keyless half is the load-bearing one: a key means a
    cascade, and a cascade would delete the record of work that already happened.

The CHANGELOG entry is sharpened, not added. #339 already recorded the breaking change; this
names the shape it replaced (FileResponse) and the route the archive moved to, so the entry
reads without the PR open beside it.

Which doc, and why not a new one

AGENTS.md says to update the doc that covers the topic and create one only if none does. #339
created docs/background-jobs.md, so this extends it. Export behaviour is documented in
docs/releases.md and the layout in docs/workspaces.md, so the retention policy is stated in
both and cross-referenced from the jobs doc rather than restated a third time.

Found, and fixed because it is the same paragraph

docs/workspaces.md still credited the export route with clearing the output directory.
#339 moved that into the handler, so the sentence had been wrong since d39eeef — in the exact
paragraph this change annotates. Corrected rather than left, and called out here rather than
slipped in.

Found, not fixed

  • Another session holds ../visionset-rename-sweep with uncommitted edits to this same skill
    file
    (refactor-protocol/SKILL.md), adding three bullets about rename sweeps. Their edit is
    in Scope discipline; mine is in Testing requirements, so the two hunks do not overlap and
    whichever lands second should merge cleanly. Flagging it because a clean merge is not a
    guarantee — see the very rule this PR adds. I did not touch their worktree. cf. feat(cli): rename server start command from visionset ui to visionset server #333.
  • docs/ingest.md describes ingest progress as living on the ingest_job row, which is true and
    now carries the "two rows for one run" caveat An embedded job system: a JobQueue port, a SQLite queue, and a spawn dispatcher (#328) #339 gave it. Collapsing the two rows is the real
    fix and remains a migration with its own wire-contract discussion. Untouched here.

Test plan

No source changed, so nothing executable moved. ruff format --check . is the gate with real
coverage here — it formats Python blocks inside Markdown, and docs/background-jobs.md contains
one.

  • ruff check . — pass
  • ruff format --check . — pass, 299 files
  • mypy src/visionset — pass, 131 files
  • lint-imports — 3 contracts kept
  • node --test "tests/scripts/*.test.mjs" — 33 pass, 0 fail
  • pnpm -r build — pass
  • pnpm -r test — 1289 pass (annotator 769, ui-core 520)
  • uv run pytest tests/architecture tests/packaging — pass (the only Python modules a docs
    change can reach; both read git ls-files)

⚠️ The local gate used the declared fallback — the rule this PR adds, applied to itself

bash scripts/check.sh did not complete. It was started and abandoned; everything above is
the sanctioned substitute. Evidence, as the rule requires:

load averages: 45.10 46.39 47.73
24   CPU spin loops   (another session, deliberately loading the box to reproduce #332)
35   pytest/vitest    (visionset-brace-expansion, visionset-332-race-gate,
                       visionset-rename-sweep, visionset-223-polyline-kernel)
43 / ~2500 tests completed in roughly ten minutes before I stopped it

Four other sessions own this machine right now. At that rate the Python suite alone was hours
away, and a timing-sensitive suite under load 45 would not have been worth trusting if it had
finished. Full green CI on clean runners is the arbiter for this PR. No source changed, so
the executable risk is close to nil — but "close to nil" is exactly the reasoning the new rule
exists to stop being silent about, so it is stated rather than assumed.

Found, not fixed (second)

Running the node gates in a fresh worktree failed 9 of 33 — every one of them node_modules
missing (Cannot find package 'openapi-typescript', and the annotator boundary gates needing
tsc/eslint). pnpm install fixed all nine. That is #336 — check.sh stepping over a
missing node_modules instead of failing loudly — reproduced here from the other direction.
Untouched; it has its own issue. cf. #336.

…k gate

Two rules #339 paid for.

A rename is a whole-tree fact. #339 renamed a test double, #281 landed
mid-flight with a fixture annotated on the old name, git merged both without
a conflict because they are different lines, and every targeted pytest module
passed — an annotation is never evaluated. CI answered F821. So: after a
rebase that brings in commits you did not write, `ruff check .` over
everything, not over what you edited.

And the local gate now has a stated fallback rather than an unstated one.
A green `check.sh` is still what a merge requires; when another session
demonstrably owns the machine, the substitute is every static gate, the full
frontend suite and the affected pytest modules, with green CI as arbiter —
declared in the PR body, because a merge that lets a reader infer a local run
that never happened is a protocol violation rather than a shortcut.
The CHANGELOG already recorded the breaking change; this names the shape it
replaced (`FileResponse`) and the route the archive moved to, so the entry
reads without the PR beside it.

The two open review items are closed as decisions rather than left as
questions somebody re-opens:

`exports/` has no retention policy, and that is the policy. No TTL, no cap,
no sweeper, no DELETE route — the disk is the user's, and a local-first tool
deleting an exported training set on a schedule nobody chose takes an action
nobody can undo. It is the posture blobs and uploads already have; what it
costs is that a workspace only grows, and `rm` is the remedy for the two
directories that are reproducible.

The `job` table has no `project_id` and no foreign key at all. A job is
workspace-scoped plumbing and its subject lives in the payload, so the column
would be null for some types and wrong for others — and the *key* would
cascade away the record of work that already happened.

Also corrects drift #339 introduced: `docs/workspaces.md` still credited the
export route with clearing the output directory, which the handler does now.
@JArmandoAnaya
JArmandoAnaya enabled auto-merge (squash) August 5, 2026 06:41
@JArmandoAnaya
JArmandoAnaya merged commit b6da8e5 into main Aug 5, 2026
14 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the docs/339-followup branch August 5, 2026 06:50
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