feat(queue): Piece D, reviews as issue comments, raw per-seat token helper (row 12, #1508)
queue move ID in-review posts the review request as a Gitea comment and review record reads verdicts back, so reviews stop being files in docs/plans/reviews/. On a comment round, in-review to waiting-on-jason now needs every listed reviewer's approval for the current round, the same as in-review to done (Filbert r1 C1). scripts/gitea-api.sh reads the raw per-seat token files (lead decisions 37 to 39): config built and checked before curl starts, export attribute cleared, fixed base URL. test-queue.sh skips its live checks outside the canonical root. Darkwing authored. Filbert approved D r2 (cf1d3fd0) after r1 (a2dc2302) and corrected the plan (293747cd). Rocko reviewed the helper (e896192f, 2096b0a3), and Sage's lead check passed under decision 38. Manifest b402fb38, 19 files. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+86
-10
@@ -33,6 +33,8 @@ scripts/test-queue.sh
|
||||
| `sync [--op ID]` | yes | fsyncs and the witness; logs nothing |
|
||||
| `snapshot --out DIR` | yes | copies of both files into an empty DIR outside the repository |
|
||||
| `unlock [--check-gate]` | no; takes the unlock gate | removes a dead or mismatched lock |
|
||||
| `review request`, `review resolve`, `review abandon`, `review record` | yes | one log entry, the witness, the table; a request also posts one comment and logs its outcome |
|
||||
| `review verify-commit ID REF` | no | nothing; reads the queue and git objects |
|
||||
|
||||
Every change takes `--op ID` (8 to 72 characters of `[a-z0-9._-]`,
|
||||
starting with a letter or digit, not ending in `.outcome`) and an actor from `--by NAME` or
|
||||
@@ -52,12 +54,13 @@ or genesis from the map), it keeps the part still among the new issues, and
|
||||
the receipt ends in `(kept narrowed)`.
|
||||
|
||||
Exit codes: 0 ok; 1 the operation failed; 2 invalid data or refused;
|
||||
3 uncertain (visible or durable, not acknowledged); 4 usage.
|
||||
3 uncertain (visible or durable, not acknowledged; for a review request,
|
||||
not known to be posted); 4 usage.
|
||||
|
||||
Until piece D, `move ID in-review` needs `--candidate`: an existing file is
|
||||
read as a manifest (one `<sha256> <path>` line per file), anything else as a
|
||||
commit reachable from `refs/heads` or `refs/tags`. The candidate is frozen
|
||||
for the round.
|
||||
`move ID in-review` needs `--candidate`. An existing file is read as a
|
||||
manifest (one `<sha256> <path>` line per file), anything else as a commit
|
||||
reachable from `refs/heads` or `refs/tags`. The candidate is frozen for the
|
||||
round.
|
||||
|
||||
The review's issue follows lead decision 23. A row with no issues can't
|
||||
request review. A row with one issue uses it. A row with several needs
|
||||
@@ -66,10 +69,72 @@ unless `--issue` names another; if the row no longer lists the kept issue,
|
||||
the request refuses until `--issue` names one. Each round records the issue
|
||||
it used (`review.rounds[].issue`).
|
||||
|
||||
`move ID done` from in-review needs `--evidence
|
||||
comment=<id>,round=<n>,candidate=<digest>`. The round must be the current
|
||||
one and the digest its candidate's, so a comment from an earlier round
|
||||
can't close a later one, even when the candidate is the same.
|
||||
A row with reviewers opens a request round and posts the review request
|
||||
comment; see "Review requests". A row with no reviewers opens a round that
|
||||
posts nothing. For that kind of round, `move ID done` from in-review needs
|
||||
`--evidence comment=<id>,round=<n>,candidate=<digest>`. The round must be
|
||||
the current one and the digest its candidate's, so a comment from an
|
||||
earlier round can't close a later one, even when the candidate is the same.
|
||||
|
||||
## Review requests
|
||||
|
||||
Piece D, section 8.9 of the plan. `move ID in-review` from in-progress, on
|
||||
a row with reviewers, logs the new round and a request attempt in the `requesting`
|
||||
state, then posts one comment on the round's issue as the acting seat and
|
||||
logs what the transport said as a second entry, `<op>.outcome`. `review
|
||||
request ID` makes a new attempt in the current round after a failed or
|
||||
abandoned one.
|
||||
|
||||
- **Credentials.** The request posts with the acting seat's own token file,
|
||||
named by `MOSAIC_GITEA_CREDENTIAL_FILE`. It must end in
|
||||
`/agents/<login>/secrets/gitea-mosaicstack-<login>.token`, be a regular
|
||||
file (no symlink, and its real path ends the same way), belong to this
|
||||
user and be mode 0600. The lead's login is `jarvis`. The shared
|
||||
`~/secrets/mosaic.gitea.json` refuses. The queue checks the file with
|
||||
`lstat` and never opens it; `scripts/gitea-api.sh` reads it. Before the
|
||||
POST, `GET user` must name the seat's login.
|
||||
- **Outcome.** HTTP 201 with a comment id is `posted` (exit 0). HTTP 400,
|
||||
401, 403, 404 or 422, or a failed pre-send check, is `failed` (exit 1).
|
||||
Anything else is `uncertain` (exit 3): another status, 201 without an id,
|
||||
a transport error, or no answer before the 30-second deadline, which kills
|
||||
the helper and curl with it. The log records the status and a fixed
|
||||
detail, never the response body.
|
||||
- **Retries never post.** Running the same `--op` again prints what the log
|
||||
holds and sends nothing. A request left `requesting` (the process died
|
||||
after the log entry) or `uncertain` needs a person to look on the issue
|
||||
for a comment carrying `<!-- mosaic-queue-op: OP -->`. If it is there,
|
||||
the owner or a privileged actor runs `review resolve ID OP --comment N`.
|
||||
The queue fetches that comment and refuses unless it is on the round's
|
||||
issue, written by the requester's login, and carries both markers for
|
||||
this attempt, round and candidate. If it is not there, a privileged actor
|
||||
runs `review abandon ID OP --reason TEXT --yes`. An abandon sets the
|
||||
round's `duplicateRisk`, because the comment may still land.
|
||||
- **Late outcomes.** An outcome that arrives after a resolve with the same
|
||||
comment id keeps `posted`. One that disagrees, or a posted outcome after
|
||||
an abandon, makes the attempt `conflict`, which a resolve settles. The
|
||||
outcome entry is the one entry a done row still accepts, so a late answer
|
||||
is recorded even after the row closed.
|
||||
- **Unresolved requests** (`requesting`, `uncertain`, `conflict`) block a
|
||||
new request, a new round, a move to `waiting-on-jason` and `done`.
|
||||
- **Verdicts.** Each listed reviewer other than the owner posts a comment
|
||||
on the issue, then runs `review record ID --verdict approve|changes
|
||||
--comment N --candidate DIGEST`, once per round. DIGEST must be the
|
||||
round's candidate. On a request round, `move ID done` and
|
||||
`move ID waiting-on-jason` each need an approval recorded by every
|
||||
reviewer in the current round, so a Jason-gated row reaches Jason only
|
||||
after its reviewers approved. `done` also refuses `--evidence`. The queue
|
||||
doesn't fetch the verdict comment; `record` is cooperative like every
|
||||
other actor claim.
|
||||
- **`review verify-commit ID REF`** checks a prospective commit or tree
|
||||
against the current round's candidate. For a manifest, every listed path
|
||||
must hash to its digest. For a commit candidate, every path the candidate
|
||||
changed from its first parent must match in REF by blob and mode, and
|
||||
every path it deleted must be absent.
|
||||
|
||||
Each log entry records the `semantics` it was written under. Entries from
|
||||
semantics 1, before piece D, replay under the old rules: a move to
|
||||
in-review opens a round that posts nothing. Review entries need
|
||||
semantics 2.
|
||||
|
||||
## Where the files live
|
||||
|
||||
@@ -223,6 +288,12 @@ reporting, so a write in progress is never reported as lost history.
|
||||
witness continuity. Canonical `verify`, under the lock, does that.
|
||||
- **Commit candidates** stay retrievable only while some ref keeps the
|
||||
commit. The queue keeps the manifest text, not the source bytes.
|
||||
`review verify-commit` on a commit candidate needs that commit.
|
||||
- **Post to outcome.** A kill after the POST and before the outcome entry
|
||||
leaves the attempt `requesting` with a comment on the issue. The queue
|
||||
never posts on its own again; `review resolve` records the comment.
|
||||
- **Verdict comments** are not fetched. `review record` trusts the
|
||||
reviewer's comment id the way the queue trusts `--by`.
|
||||
- **Locks** are never removed for their age. `unknown` and `invalid` locks
|
||||
wait for a person. A host rename makes old locks `unknown`.
|
||||
- `queue unlock --check-gate` classifies a stale unlock gate. Remove the
|
||||
@@ -264,7 +335,7 @@ then `queue render`.
|
||||
`node --test packages/queue/tests/` runs everything in scratch repositories
|
||||
under the system temp directory; nothing touches this checkout's `.git`.
|
||||
Faults reach the code only through options the tests pass in (`io`, `proc`,
|
||||
`hook`, `now`, `readOrder`, `lockWaitMs`); the CLI passes none.
|
||||
`hook`, `now`, `readOrder`, `lockWaitMs`, `deadlineMs`); the CLI passes none.
|
||||
|
||||
- `data.test.mjs`: serialization, replay, the transition matrix, `next`
|
||||
ordering, render.
|
||||
@@ -280,3 +351,8 @@ Faults reach the code only through options the tests pass in (`io`, `proc`,
|
||||
`map-check.mjs` reports each kind of drift.
|
||||
- `commit.test.mjs`: `queue-commit.sh` and the guard, through PATH shims
|
||||
that run an action at an exact point in the procedure.
|
||||
- `review.test.mjs`: review requests against `fixtures/fake-gitea.mjs`,
|
||||
installed as the scratch repository's `scripts/gitea-api.sh`, with dummy
|
||||
token files. Every transport answer, the deadline, kills at each step,
|
||||
a held lock at the outcome, late outcomes, resolve checks, credential
|
||||
checks, verdicts across rounds, `verify-commit` and semantics 1 replay.
|
||||
|
||||
Reference in New Issue
Block a user