The initial import copied the runner LICENSE, which credits the Gitea and
GitHub authors for the vendored Actions protocol and the hashFiles bundle.
Neither lives here, so document where they do and state the provenance rule.
Co-authored-by: Cursor <cursoragent@cursor.com>
The README explained why other vendors are named in the source and how to
embed the repository as a submodule. The first is noise, the second is
wrong: consumers depend on a git URL pinned to a commit.
Co-authored-by: Cursor <cursoragent@cursor.com>
The repository ships no image, so a Dockerfile only to compile it was a
layer with nothing to produce. The job image already carries cc, git and
curl, so the toolchain action is all that is missing.
Co-authored-by: Cursor <cursoragent@cursor.com>
The runner cannot resolve a ref that looks like a version number, and a
commit is the reference an immutable action should carry anyway.
Co-authored-by: Cursor <cursoragent@cursor.com>
The compiler already reads action references, so the model, the reference
and the action.yml parser belong next to it. Repository URLs and revisions
come along because a reference is expressed in them.
Co-authored-by: Cursor <cursoragent@cursor.com>
Node 20 is the minimum SynCode ships, so an action declaring node12 or
node16 is rejected instead of being silently upgraded. With the legacy
variants gone the declared runtime and the shipped runtime are one enum.
Co-authored-by: Cursor <cursoragent@cursor.com>
The plan model gains serde, so the plan the control plane compiles is the plan
the runner decodes rather than a copy of it converted at a boundary. Validated
types deserialize through their own constructors, expressions travel as source
and are rebuilt by parsing, and VersionedPlan refuses a schema version this
build does not speak.
A plan that travels needs the same two things a workflow file needs: which
dialect produced it and the bytes. Keeping the payload opaque lets a plan be
moved without knowing which expression type it holds.
domain_map! is exported and used by consumers whose maps hold types that do not
serialize. Only DynamicObject travels inside a plan, so only DynamicObject gets
the impls.
dtolnay/rust-toolchain publishes versions as branches whose heads move, so a
commit pinned today is unreachable from any ref once the branch advances and a
fresh mirror no longer carries it. The branch is what a mirror can serve.
The runner on production cannot resolve an action reference named like a
version, so dtolnay/rust-toolchain is unusable there until it ships the fix.
The toolchain is pinned by image digest instead, which the job container
resolves without any action at all.
The compiler accepted only an already-specialised matrix, because the forge
expanded it before the runner ever saw it. The control plane compiles the
workflow as written, so a dimension now holds all its values and expansion
turns one plan into one plan per combination.
A node is still only ever handed a plan with a single value per dimension:
expansion happens before assignment, never on the node.
Nothing modelled workflow triggers, because the forge decided when to start a
run and the runner never asked. The control plane has to decide instead: an
event states its kind, its branch and what it touched, and a trigger admits it
by branch and path.
An exclusion always wins, a workflow that says nothing is never triggered, an
event that touched nothing cannot satisfy a path filter, and glob syntax this
compiler does not implement is refused rather than matched by accident.
The reader dropped 'on:' because the forge decided when a run started. It now
becomes typed triggers: a single event, a list, or a mapping with branch and
path filters. An event this control plane cannot act on is refused when the
workflow is read, rather than read as a workflow that triggers on nothing.
A reusable workflow call was a string the compiler refused wholesale. It is now
addressed: a local call names a path in the same commit, a remote one names a
repository, a path and a revision, and a call naming no workflow file or no
revision is refused when it is read.
The compiler still refuses to expand either — that belongs to the control plane
— but it now says which kind it is refusing, which is the difference between
needing the commit's other files and needing a fetch.
A workflow that can be called says so with on: workflow_call, and the reader
refused it as an event nobody acts on — which made every callable workflow
unreadable, and reusable calls unusable in practice.
It now reads as what it is: being callable is not something that happened, so it
declares no trigger and fires on nothing.
`on.schedule` is a sequence of cron entries, because a schedule says when it
fires rather than what it fires for. Every trigger was read as a mapping,
so a workflow spelled exactly as the dialect prescribes was refused, and the
control plane would have turned away every scheduled workflow there is. The
runner's own compatibility workflow is one of them.
A schedule now reads as the sequence it is, with no branch or path filter to
take from it. A shape that is not a sequence is still refused rather than
guessed at.
The trigger tests move to a file of their own; there are enough of them now
that they were pushing the model tests past the line limit.
A plan holds a single job, because a node executes a single job: an assignment
names one, reports one, and holds a lease on one. What did not follow is that a
workflow is one plan. It is as many as it has jobs.
Lowering refused anything else, and that refusal was the whole of the gap
between what this compiler produces and what a forge running the same
repository produces. Measured against the five SynCode repositories, the two
that diverged both failed here and nowhere else, on four jobs and on five.
Everything the jobs need was already carried: the HIR holds them all, and each
one states what it waits for. So each becomes its own plan, keeping its
`needs`, and what is left to whoever queues them is the order.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Promote the verified SynCode 0.3 implementation to the release branch.
Approved for the verified SynCode 0.3.0 release.