Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions docs/LIVE-REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,13 @@ effective stage). The rules are in `src/signable.ts`:
Review 2.7.0 asks only for a declared subject and refuses any other with
`JOB_ASK_INVALID` (`bindJobAsk`, `src/job-act.ts`). Both the ask and the act
also require `project.json` to still hold the published bytes
(`assertJobSourceCurrent`, `src/job-source.ts`). So the person signs on the
publication of the record that can sign, before any later write.
(`assertJobSourceCurrent`, `src/job-source.ts`). So either the owner submits
both the verdict and `signoff` acts in the Review UI before any writer mutates
`project.json`, or after a CLI writer (`parts-review --from-review`,
`diff-classes`, or `review <n> --from-review`) updates `project.json`, the
agent runs `rig-create job open` to republish the current selector (`draft`,
`rig`, or `post`) over the updated `project.json` bytes and re-ask the stage's
declared subjects before the owner signs off in the UI.

`job reconcile` keeps the declared subjects in the acknowledged receipt.
`signoff … --from-review` compares the act's subject with that list and
Expand Down Expand Up @@ -302,9 +307,9 @@ again.
| `parts` checkpoints | draft | `draft-rig` commits while the record is in `parts` and the checkpoint is enabled |
| `rigging` checkpoints | rig version | `rig-version` commits in `rigging` |
| `post` checkpoints | post version | `direction write` or `direction patch` commits in `post` |
| `parts-review` | draft | `draft-rig` commits in `parts-review` |
| `rig-review` | rig version | `advance rig-review` commits the move into `rig-review`, or `rig-version` commits in `rig-review` |
| `direction-review` | post version | `advance direction-review` commits the move into `direction-review`, or `direction write` / `direction patch` commits in `direction-review` |
| `parts-review` | draft | `draft-rig` commits in `parts-review`, or the agent runs `job open` to refresh the draft after `parts-review --from-review` / `diff-classes` |
| `rig-review` | rig version | `advance rig-review` commits the move into `rig-review`, `rig-version` commits in `rig-review`, or the agent runs `job open` to refresh the live `rig` head after `review <n> --from-review` |
| `direction-review` | post version | `advance direction-review` commits the move into `direction-review`, `direction write` / `direction patch` commits in `direction-review`, or the agent runs `job open` to refresh the live `post` head after `review <n> --from-review` |
| `rigging-interview`, `post-interview` | any package publication with plan steps | only when a package writer commits in that stage, which the usual order does not do |
| `interview`, `plan-approved` | none | no draft or version exists before they are signed |

Expand Down
29 changes: 15 additions & 14 deletions src/job-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,11 +332,11 @@ function selectorLabel(selector: JobSelector): string {
}

/**
* After `job open`: show the record's current draft on the owner's page. The latest
* recorded draft (or `--draft <id>`) is published unless the job's head publication is
* already that draft over the current record bytes, and every subject the publication
* declares is asked once. An already-acknowledged `rig` or `post` head also retries any
* unasked declared subject without being overwritten by a draft. Returns the lines `job open` prints.
* After `job open`: show or refresh the record's current publication on the owner's page.
* The latest recorded draft (or `--draft <id>`), or the head's `rig` / `post` selector at
* later stages, is published unless the job's head publication already covers that selector
* over the current record bytes, and every subject the publication declares is asked once.
* Returns the lines `job open` prints.
*/
export async function openDraftPublication(root: string, host: JobOwnerHost, now: number, requested?: string): Promise<string[]> {
root = realpathSync(root);
Expand All @@ -347,12 +347,13 @@ export async function openDraftPublication(root: string, host: JobOwnerHost, now
const source = head?.source as { selector?: JobSelector; projectHash?: unknown } | undefined;
const acknowledged = head ? readAcknowledgedPublication(root, host.session.jobId, head.publicationId) : null;
const headSelector = acknowledged?.result.selector ?? source?.selector ?? null;
const reuseHead = Boolean(head && headSelector && (
requested !== undefined
? isDeepStrictEqual(headSelector, { kind: 'draft', id: requested }) && source?.projectHash === jobHash(bytes)
: headSelector.kind !== 'draft'
? draftId === undefined || source?.projectHash === jobHash(bytes)
: source?.projectHash === jobHash(bytes) && (draftId === undefined || headSelector.id === draftId)
const targetSelector: JobSelector | undefined = draftId !== undefined
? { kind: 'draft', id: draftId }
: headSelector && headSelector.kind !== 'draft'
? headSelector
: undefined;
const reuseHead = Boolean(head && headSelector && source?.projectHash === jobHash(bytes) && (
targetSelector === undefined || isDeepStrictEqual(headSelector, targetSelector)
));
let selector: JobSelector;
let outcomes: AskOutcome[];
Expand All @@ -365,18 +366,18 @@ export async function openDraftPublication(root: string, host: JobOwnerHost, now
selector = headSelector;
published = false;
} else {
if (draftId === undefined) {
if (targetSelector === undefined) {
return ids.length === 0 ? ['job: no recorded draft to publish'] : [`job: stage ${stage} shows no draft; name one with --draft <id>`];
}
const result = draftPublicationResult(draftId);
const result = selectorResult(targetSelector);
const release = acquireJobLock(root)!;
try {
const binding = readWriterBinding(root);
if (!binding || binding.jobId !== host.session.jobId) refuse('JOB_BINDING_INVALID', 'project.json: the open job has no writer-owned binding; run rig-create job close, then rig-create job open');
enqueuePublication(root, binding, result);
({ outcomes } = await reconcileLocked(root, publishPending));
} finally { release(); }
selector = result.selector;
selector = targetSelector;
published = true;
}
const lines = [`job: ${selectorLabel(selector)} ${published ? 'published' : 'already published'}`, ...askOutcomeLines(outcomes)];
Expand Down
Loading
Loading