🏭 Complete the software-factory contract above PR #630 - #736
Open
minkimcello wants to merge 5 commits into
Open
Conversation
Turn PR taras#630's draft into a decision-complete first-release architecture. No product, deployment, authority, identity, ownership, failure, replay, public form, permission or ordering decision is left for a later Implementor. The factory specification now settles: the canonical `github-issue-v1` run-ID derivation and unsupported issue-transfer drift; `User` as the only Stage 1 spelling; the deployment topology of one SQLite-backed Cloudflare Durable Object, an authenticated executor WebSocket, an ephemeral Actions runner and a dedicated GitHub App; authenticated ingress order, bounded intake, dispatch payload and OIDC claims; the exact App permissions, host ceilings, human permission floor and `.github/workflows/**` denial; suspend-on-every-conflict as the single Stage 4 profile; the Stage 7 trusted merge, `Git.PublishTarget` compare-and-swap and remote-effect-before-terminal ordering; and the exact public contract inventory. Section 8's remaining material decisions are gone. architecture.md gains the terminology, the remote storage and executor topology, the delivery-plane generalization, the Project-provider boundary, comments/readiness/closure, ordered merge, target publication, trusted evidence execution, terminal settlement, the split trusted host, and ten construct inventory rows marked "specified by taras#710; implementation unbuilt". The workflow and Workspace specifications gain the remote host, the executor connection, which requests need it and which do not, the host-derived public run ID, and provider-neutral contracts for `Git.Merge`, `Git.PublishTarget`, `PullRequest.Comment`/`Ready`/`Close`, `Issue.Comment`/`Close`, `Evidence.Run` and `Project.Status` — none of them reachable by an Agent or generated XMD, whose write table stays exactly `File:write`, `Dir` and `File.Delete`. The executable-MDX specification gains the exact authored forms and the frozen WRH/WGI/WGE/WGM/WER/WFP/WFL acceptance tiers. Documentation only: no production code, workflow YAML, dependency, generated artifact or executable fixture changes, and the ownership SVG already agrees with the amended text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP9dEis51dcwBnwdaXZQAg
Answer the Architect's three blocking findings on 86a4694. The public contracts are now closed shapes rather than descriptions of shapes, run identity has one spelling, and the two terminal paths are separate everywhere. Finding 1 — exact bindings and durable records. specs/workflow-workspace-spec.md is the normative schema location for every authored construct: §7.8 defines the `GitMergeResult` clean/conflicted union, the conflict-entry record, its sorting, duplicate and missing-side rules, and restoration failure; §7.9 defines the `Git.PublishTarget` request and result with five exhaustive pre-states; §7.10 defines the comment request/result and how an engine-derived effect identity is made remotely observable without using body text, plus literal `Ready`/`Close` bindings; §10.3 does the same for `Issue.Comment` and `Issue.Close`; §10.5 settles `Evidence.Run` — the whole list runs, separate bounded stdout and stderr with stated truncation, and an explicit table of which cases bind, which fail and which commit nothing, with cancellation and teardown precedence fixed; §10.6 defines `Project.Status`. The factory specification §11.2 defines the versioned closed factory protocol schemas — subject, stage, revision, handoff, actor, outcome, invalidation, verdicts, conflict suspension, Stage 7 decisions, the frontier and its complete reduction table, and the two asymmetric terminals. Finding 1 also adds `PullRequest.Merged` (§7.11), the reconciled Git-host observation that owns the post-publication merged fact, and settles the remote host boundary (§13.2): the existing four-method `WorkflowHost` stays the host assembly contract with a Cloudflare implementation beside the Deno one, the runner-to-owner transport is a closed versioned envelope that refuses rather than adapts, and an ownership table says which side owns each concern. Finding 2 — every restatement of the run-ID derivation now uses `canonical GitHub authority` byte for byte; the undefined Issue-provider spelling is gone. Finding 3 — architecture.md and the factory specification now state the merged and abandoned paths as separate ordered step lists, and the terminal record's two shapes differ exactly as the paths do, so neither can be read as requiring the other's effects. Inventories, evidence tiers and the acceptance checklist are reconciled with all of it. Documentation only; the ownership SVG is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP9dEis51dcwBnwdaXZQAg
Apply rulings A-K from the Architect's answers on 80884f9. A. Keep the four-method `WorkflowHost` and add that its transition and request types — `WorkflowExecutionTransitions`, `WorkflowBeginRequest`, `WorkflowExecutionBegun`, `WorkflowForkRequest`, `WorkflowForkSelection`, `WorkflowRunCreation` — are provider-neutral and become package-root public types, with runtime-named entrypoints keeping implementations and retained encodings. Recorded as authorized future export work, not performed here. B. `Evidence.Run` becomes an ordered fail-fast pipeline binding the executed prefix: `{ completion, authoredCommands, executed, runTimeout? }`, rows carrying `limit` for whichever of the two host-owned ceilings fired, a `runTimeout` record for a whole-run expiry between commands rather than a fabricated argv row, timeouts as ordinary unsuccessful outcomes, and fixed precedence — cancellation over everything, first infrastructure failure authoritative with a later teardown failure as secondary evidence, teardown authoritative alone — with bounded diagnostic evidence retained on a failed effect's Error. C. A comment provider must support a stable opaque correlation marker or refuse before its first mutation. The authored logical body is preserved byte for byte and the correlation representation rides outside it. Absence is now judged against attempt state: no marker after an attempted-but-uncommitted creation is permanent ambiguity, not proven absence. D. `PullRequest.Merged` splits still-open (temporary unavailability) from closed-unmerged (conflict), and the factory gains §10.4 — a bounded host-configured retry, then a durable machine wait with its own protocol record, woken by intake or an explicit resume and carrying no verdict. E. A configured total bijection between the nine stages and nine exact Project status option IDs, validated against a complete reread and refusing before any intake, token, run or projection. F. `Git.Merge`'s `purpose` is validated authorization against the provider-authenticated merge ceiling, not write-only provenance. G. The `RunnerRequest` envelope is deleted. The runner and owner ship as one release identity checked by build fingerprint at admission; the messages are private, while the owner-side authority invariants stay public and exact. H. `FactoryTerminal` keeps journal event references, with cross-path, kind, completeness and agreement validation spelled out. I. The factory protocol schemas stay in the factory specification §11.2; the other four documents link rather than duplicate. J/K. Mixed wrapping left alone, new prose unwrapped; the tenth construct is reflected everywhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP9dEis51dcwBnwdaXZQAg
taras#710) Ruling C asked for the comment contracts, the architecture, the WGE evidence and the generic external-effect explanation to agree. The first three landed in 0d08dcc; §10.2 did not. Split the two kinds of reconciled effect where the difference actually lives. An effect that mutates a subject already there — Push, a numbered pull-request update, ready, close, issue close, Project status — reads that subject, and a complete observation is decisive whether or not the effect has attempted anything. An effect that creates a new object the host names has nothing pre-existing to read and no client-supplied idempotency key, so its completion is visible only through a correlation value it wrote itself, and absence means a different thing before and after a mutation has been attempted. Only the second kind carries attempt state, and it narrows the decision in one place: unattempted with nothing found performs once, while attempted with no committed completion and nothing found is permanent ambiguity rather than absence. A provider that cannot write, preserve and completely query such a value refuses the effect from observation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP9dEis51dcwBnwdaXZQAg
Four narrow corrections from the Architect's verdict on b364b98. 1. The merged-observation wait is not a typed-answer suspension. A machine wait is a second durable wait kind inside the existing lifecycle: it asks nobody anything and ends because a later execution observed a provider again, so it has no response schema, no `xmd workflow answer` route, no form and no bound value, and it publishes no `suspension_request` and consumes no `suspension_answer`. It shares only the atomic boundary — its `machine_wait` event and the `suspended` status commit together, and the acquisition is released after that commit. Its identity is a `waitId`, never a suspension ID; `MergedObservationWait.suspensionId` becomes `waitId`, while `ConflictSuspension.suspensionId` and the typed-answer protocol are untouched. A new `MergedObservationWake` names the same `waitId` and a closed `source`, with `intakeId` required exactly for a provider intake and absent exactly for an operator resume. Waking is permission to look again: the intake retains a bounded notification carrying no answer, verdict, stage, transition or observation result, a later executor consumes it and appends the wake event in one transaction, and a resume without a pending wake or operator authority reports the same wait and settles suspended again. 2. The generic create-effect rule now distinguishes its two safe mechanisms. An effect with a provider-native client key — an Issue upsert, a pull-request upsert — reconciles on that key and carries no attempt state; creating an object is not by itself what makes an effect attempt-stateful. A comment has neither a native key nor a pre-existing subject, which is why it needs the marker and the unattempted/attempted distinction. An effect with neither mechanism refuses before its first mutation, as an intentional constraint. 3. Completed replay may reach and read the run's durable owner — lifecycle storage access, not external-effect replay — while attaching no external-effect provider, performing no effect again and starting no native operation. The "no remote storage session" wording is gone from every document. 4. The authored comment body is preserved byte for byte as the authored portion of the projection, with transport metadata outside it, rather than the provider payload being claimed to equal the authored bytes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SP9dEis51dcwBnwdaXZQAg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Closes#710.
The implementation stories under #633 need one decision-complete software-factory contract before they can build provider, ingress, Git, evidence, and factory behavior without choosing architecture independently. This PR completes that contract on top of the draft architecture in #630.
What changes
Before:
The draft architecture left deployment ownership, conflict behavior, durable identity, authority, target publication, replay, and terminal outcomes incomplete or distributed across documents.
After:
The architecture and specifications define the Cloudflare Durable Object owner, connection-scoped executor acquisition, GitHub intake and permission boundaries, stable run and effect identities, exact Git and target-publication behavior, machine waits, reconciliation, replay, abandonment, and the public construct inventory consumed by F1–F6.
How it works
Review guide
Start with:
specs/github-actions-software-factory-spec.mdThen review:
architecture.mdfor topology, principals, ownership, and durable boundaries.specs/workflow-spec.mdfor provider-neutral lifecycle and host contracts.specs/workflow-workspace-spec.mdfor effect, wait, reconciliation, and replay semantics.specs/executable-mdx-spec.mdfor the frozen conformance inventory.Look carefully at:
What must stay true
How to verify it
git diff --check 0479b3b1e6a3de08fb7d9de515a3efde80e7fe91...817d3dd36cb07fddd972c6ae33716444f3cb75dareports no whitespace errors.Scope
Included
Intentionally unchanged
Risks and limitations
817d3dd36cb07fddd972c6ae33716444f3cb75daas their exact contract base and be reconciled if that contract changes.Scope confirmation