driver: implement post-target run argv semantics

This commit is contained in:
2026-08-23 17:33:31 +09:00
parent 0b24b11d65
commit 29122f2a48
6 changed files with 579 additions and 17 deletions

View File

@@ -2300,7 +2300,7 @@ The default workflow is deliberately short:
```sh
ww init example.org/hello
ww build
ww run -- argument
ww run . argument
ww test .
```
@@ -2313,8 +2313,9 @@ shown in verbose output.
`ww build [DIR|PRODUCT]` builds the default root product or one named product.
The default profile is the fully specified `debug` profile; `--profile=release`
selects the toolchain's immutable release profile. `ww run` first performs that
same build, then runs only a product with `H = B`; arguments after `--` are never
interpreted by the build.
same build, then runs only a product with `H = B`. After one explicit run target
is selected, every remaining operand is program input and is never interpreted
as a build option. A leading `--` is not the target boundary.
There is no command that means “build and opportunistically download whatever is
missing.” If a locked source or toolchain is absent, the diagnostic names its
@@ -11168,6 +11169,165 @@ source packages, shared test-process state and failure topology, RE2-compatible
flat `-run`, finite special-source handling, or external-driver interruption
recovery.
### 11.57 Implemented post-target `ww run` argument boundary
After one explicit `ww run` target has been selected, every later operand is
now program input. Neither driver reparses that suffix as build options. Known
and unknown option spellings, would-be option values, a lone `--`, empty
strings, later ordinary operands, and later `.ww` spellings retain their exact
bytes and order in the child vector. WW still supports only one selected run
target: this boundary does not turn a later `.ww` spelling into a second source
file.
#### Pinned authority, official tests, and applicability
The sole semantic authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- **behavior directly implemented or asserted by pinned Go** — the Go command
parses a run command's registered flags before entering `runRun` and passes
only `Flag.Args()` to it
([`cmd/go/main.go`, lines 312322](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/main.go#L312-L322)).
The standard flag parser stops at the first non-flag positional; it consumes
`--` only when that spelling occurs before the positional boundary
([`flag/flag.go`, lines 10741089 and 11491176](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/flag/flag.go#L1074-L1089)).
- **behavior directly implemented or asserted by pinned Go** — `runRun`
consumes either the contiguous named-file prefix or one selected package,
leaves the suffix as `cmdArgs`, and attaches those exact arguments to the run
action
([`cmd/go/internal/run/run.go`, lines 96140 and 170173](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/run/run.go#L96-L140)).
- **behavior directly implemented or asserted by pinned Go** — official
regression test
[`cmd/go/testdata/script/mod_run_flags_issue64738.txt`, lines 14](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/mod_run_flags_issue64738.txt#L1-L4)
asserts that `-p ignored` after a requested package is program input, not a
`cmd/go` flag. Official
[`cmd/go/testdata/script/run_dirs.txt`, lines 120](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/run_dirs.txt#L1-L20)
separately anchors Go's contiguous multi-file prefix; that source-set rule
remains open in WW.
- **behavior directly implemented or asserted by pinned Go** — Go deliberately
does not preserve the compiled program's exact nonzero exit status
([`cmd/go/internal/run/run.go`, lines 56 and 198210](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/run/run.go#L198-L210));
command error accounting owns the resulting Go-command status
([`cmd/go/internal/base/base.go`, lines 218246](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/base/base.go#L218-L246)).
That independent exit-status difference is not credited to this slice.
- **behavior derived from the pinned implementation** — WW's one explicit
local run target supplies the same honest semantic boundary without modules,
manifests, registries, network resolution, generalized imports, or source
build expressions. WW's implicit default-current-directory extension has no
explicit target token, so this slice leaves its leading option parsing
unchanged. A leading or pre-target `--` therefore retains WW's existing
exact unknown-flag rejection and is not claimed as Go `FlagSet` terminator
parity.
Before this slice, **directly measured WW behavior** was identical in Cstage
and WWstage but differed from the pinned boundary. Immediately target-following
`-p ignored` and `-- -p ignored` returned status 2, empty stdout, and exact
stderr `ww run: unknown flag\n`; `-o sentinel` and `-I path` were consumed by
the driver; and only a suffix after a second nonflag was passed unchanged.
Named-source and directory-package probes agreed on status, stdout, stderr,
and diagnostic order. Accepted runs loaded one target and its ordinary import
closure, constructed the private run action, compiled, assembled, archived,
linked, executed, propagated WW's established child status, and removed the
private executable and `.sepwork`. Rejected flag rows stopped before target
resolution, graph/action construction, tools, runtime, or scratch creation.
#### Ownership, source selection, and the four permanent axes
- **directly measured WW behavior** — Cstage owns the boundary in
`parse_build_flags` and its `do_run` consumer; WWstage owns the semantic twin
in its `dorun` parser and executor. No loader, package coordinator, compiler,
assembler, archiver, linker, test coordinator, checker, import binder, or
runtime library can repair a driver option that was already consumed.
- **behavior derived from the pinned implementation** — the primary axis is
build/run execution semantics. Driver options are recognized before the
first explicit target. That target alone controls resolution; the complete
later suffix controls only child invocation.
- **behavior derived from the pinned implementation** — the test axis is an
explicit non-effect. `ww test` option parsing, target discovery, filters,
source variants, generated harness, retained products, execution topology,
and result accounting do not use this run boundary.
- **behavior derived from the pinned implementation** — the package axis is an
explicit non-effect. The suffix is never searched, statted, opened, or
classified as source. Declared-name validation, source membership, command
classification, initializer topology, and canonical package identity remain
those of the one selected target.
- **behavior derived from the pinned implementation** — the import axis is an
explicit non-effect. Dotted spelling, aliases, local/vendor search, binding,
visibility, cycles, graph edges, interface ownership, and initialization
order are determined only by the selected source closure. Runtime argv is
never package, import, graph, action, symbol, artifact, `.wwi`, publication,
or persistence identity.
Direct roots retain `__root.*`; dotted directories retain dotted package,
import, action, symbol, artifact, and semantic identities. Physical target
spellings and paths remain loader or presentation observations. Post-target
arguments add no root, edge, action, source, or invalidation input and cannot
change compilation or comparable artifact bytes.
#### Phase order, diagnostics, and lifecycle
- **behavior derived from the pinned implementation** — target resolution,
loading, import closure, graph construction, compilation, assembly,
in-process archiving, and linking retain their existing order and inputs.
After a successful private link, the child vector is the private executable
at index 0 followed by the exact post-target suffix. The suffix reaches no
earlier phase.
- **directly measured WW behavior** — `os.args()` exposes that complete vector,
including the PID-bearing private executable path at index 0. WW currently
propagates an ordinary child's exact exit code. The argument repair changes
only indices 1 onward; PID presentation and exact child-status propagation
are preserved, including their independent difference from pinned Go.
- **behavior derived from the pinned implementation** — known, unknown,
incomplete, or `--` option spellings before the target retain their existing
driver diagnostics and status. No spelling after the target can emit a
driver-option diagnostic. Missing, invalid, non-main, or producer-failing
targets diagnose before runtime; a valid target starts and thereafter owns
output and failure caused by its arguments.
- **behavior derived from the pinned implementation** — run products remain
request-private. No public executable, retained test product, semantic fact,
work record, transaction, result, or cache entry is published. Post-target
argv enters no unit, interface, assembly, object, archive, initializer,
executable, stamp, or persistence byte and creates no reuse or invalidation
key. Ordinary build and every test route are byte-for-byte non-effects.
- **behavior derived from the pinned implementation** — target or producer
failure starts no program and follows ordinary rollback. Runtime nonzero
follows the established WW status mapping after successful private linking.
Existing unrelated public and committed semantic bytes remain untouched.
Normal success, producer failure, runtime failure, and concurrent runs remove
each request's owned private executable, `.sepwork`, stage, transaction,
capture, result, request, descriptor, and child. Parser state and argv are
invocation-local, so overlapping suffixes cannot cross between requests.
- **behavior derived from the pinned implementation** — Cstage and WWstage
must select the same boundary and retain exact status, stdout, stderr,
diagnostic order, runtime argv, normal cleanup, and comparable build-artifact
byte identity. The existing PID-bearing private path difference outside
stable comparisons is not reclassified by this slice.
The WW-native observer `run_post_target_arguments_are_program_argv` covers both
driver stages with literal named-source and directory-package targets. It
checks known separate and joined option spellings, unknown options, would-be
values, singleton value-taking spellings, `--`, later nonflags and `.ww`, an
empty string, pre-target controls, no-operand default-dot selection, diagnostic
precedence, exact child status and output, concurrent isolation, build/test
controls, artifact-byte parity, and normal residue cleanup.
No signal or process-supervision owner changes. Direct external SIGTERM during
blocked persistent compilation still can leave the owned compiler alive and
exactly three fixed `.new` stages, poisoning the next request while preserving
prior public and committed semantic bytes. That verified interruption gap
remains open; blind stage deletion is not this argument-boundary repair.
No serialized representation changes. Build workdir format remains `18`, test
workdir format remains `19`, and semantic storage format remains `3`; AST and
`.wwi` schemas, action descriptors, request protocols, transaction markers,
and stored facts are unchanged. This closes only the post-target argv slice.
Regular or missing `_test.ww`, missing `.ww`, hidden named sources, multiple
leading sources and their source-set boundary, shared test-package state,
panic/exit/Fatal/FailNow topology, RE2-compatible flat `-run`, literal
nonregular named-source behavior, three-way no-buildable-source diagnostics,
Go-like run exit-status mapping, and external-driver interruption recovery
remain open where applicable.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -703,6 +703,32 @@ ImportPath = ident { "." ident } .
arguments and result; this rule does not adopt Go's source signature. An
ordinary import of a package declared `main` is rejected, except for the
toolchain's colocated external-test wiring.
- For `ww run`, the first explicit non-option operand selects the one run
target. Every later operand is program input and is passed unchanged and in
order after the private executable's `argv[0]`; it is not reparsed as a
driver option. This includes known or unknown option spellings, would-be
option values, a lone `--`, empty arguments, ordinary nonflags, and later
`.ww` spellings. A later `.ww` is therefore argv, not another source in a
command-line package; multiple named sources remain unsupported. Registered
driver options before the target retain their established spelling, value,
repetition, and diagnostic rules. A leading or otherwise pre-target `--`
retains WW's exact unknown-flag rejection rather than acting as a general
terminator. With no explicit target, `ww run` retains its implicit current-
directory selection and has no post-target suffix.
Only the selected target and pre-target driver options affect resolution,
source loading, package/import identity, graph construction, compiler,
assembler, archiver, linker, private executable bytes, reuse, or
invalidation. A missing, invalid, non-main, or producer-failing target
diagnoses before runtime and no suffix spelling can mask that result. A
successful private link executes with the exact suffix and retains WW's
existing stdout, stderr, and exact child-status propagation. The private run
product is removed normally and publishes no executable or persistent
semantic state. `ww build`, every `ww test` route, package membership, and
dotted import semantics are unchanged. Direct roots remain `__root.*`,
dotted directories retain dotted identity, and argv never becomes package,
import, action, symbol, artifact, `.wwi`, publication, or persistence
identity. Build workdir format remains 18, test workdir format remains 19,
and semantic storage format remains 3.
- For `ww build`, the output-option name is exactly `o`. The accepted forms are
`-o VALUE`, `--o VALUE`, `-o=VALUE`, and `--o=VALUE`. An equals form splits
at its first `=` and preserves every later byte, including further `=`

View File

@@ -479,6 +479,82 @@ supervision is unchanged: the known orphan compiler, fixed `.new` staging, and
later persistent-request poisoning remain open and are not credited to this
slice.
The run front has one separate post-target argument contract. The sole
authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- **behavior directly implemented or asserted by pinned Go** — command flag
parsing supplies only its positional suffix to `runRun`
(`cmd/go/main.go:312322`; `flag/flag.go:10741089,11491176`), and
`runRun` consumes the named-file prefix or one package before attaching the
untouched suffix to the run action
(`cmd/go/internal/run/run.go:96140,170173`).
- **behavior directly implemented or asserted by pinned Go** — official
`cmd/go/testdata/script/mod_run_flags_issue64738.txt:14` proves that
target-following `-p ignored` is program input rather than a Go-command
option. `cmd/go/testdata/script/run_dirs.txt:120` anchors the independent
contiguous named-file prefix, which WW does not claim here.
- **behavior derived from the pinned implementation** — after WW's one
explicit local run target, every remaining operand is runtime argv. The
rule honestly applies without importing Go modules or manifests. WW's
default-dot extension has no explicit target boundary, and leading or
pre-target `--` remains its existing unknown flag rather than gaining
general Go flag-terminator semantics.
Before the repair, **directly measured WW behavior** was stage-equal but
different: both drivers rejected immediately target-following `-p ignored` and
`-- -p ignored` at status 2 with empty stdout and exact unknown-flag stderr,
consumed target-following `-o` and `-I` values as driver configuration, and
passed the ordered suffix only after a second nonflag. Accepted controls loaded
one target and its ordinary import closure and completed the private compiler,
assembler, archiver, linker, and runtime route; rejected rows stopped before
loading, actions, tools, runtime, or scratch.
The focused `run_post_target_arguments_are_program_argv` observer owns the
completed boundary in `test/package/package_test.ww`. Across Cstage and WWstage
it uses both a literal named source and a directory package and requires exact
ordered delivery for separate and joined known-option spellings, unknown
options, would-be values, a singleton value-taking spelling, `--`, later
nonflags and `.ww`, and an empty string. It separately requires pre-target
options and their missing/unknown diagnostics to remain driver-owned, leading
`--` to keep its current rejection, no-operand run to keep default-dot
selection, and target load/producer diagnostics to precede inert suffix bytes.
Valid programs expose the private executable at `os.args()[0]`, receive the
exact suffix at indices 1 onward, and retain WW's existing stdout, stderr, and
exact child-status mapping. Pinned Go's distinct command-level nonzero status
mapping (`cmd/go/internal/run/run.go:56,198210` and
`cmd/go/internal/base/base.go:218246`) remains an open difference.
The observer also treats all four permanent axes as one contract. The build/run
axis changes only the parser-to-child boundary. Build and test controls retain
their status, streams, action inputs, and comparable artifact bytes. Package
membership and command classification still come only from the selected
target, while dotted-import resolution, edges, interfaces, visibility, and
initialization still come only from that target's closure. Direct actions
remain `__root.*`; dotted directories retain dotted identity; argv creates no
package, import, graph, action, symbol, artifact, `.wwi`, publication,
persistence, reuse, or invalidation identity.
Post-target bytes therefore cannot change compiler, assembler, archiver,
linker, initializer, or private executable bytes. Run publishes no public
product or semantic state. Normal success, target/producer failure, runtime
nonzero, and overlapping requests must keep suffixes isolated and remove each
owned private executable, `.sepwork`, stage, transaction, capture, result,
request, descriptor, and child. Cstage and WWstage must agree on stable status,
stdout, stderr, diagnostic order, runtime argv, cleanup, and comparable
artifact bytes. No AST, interface, descriptor, request, transaction, or
persistent schema changes; build workdir format remains 18, test workdir format
remains 19, semantic storage format remains 3, and no test-result cache is
introduced.
The observer makes no claim for regular or missing `_test.ww`, missing `.ww`,
hidden named sources, multiple leading source operands and source-set
boundaries, literal nonregular named sources, shared test-package state,
panic/exit/Fatal/FailNow topology, Go-compatible RE2 `-run`, three-way
no-buildable-source causes, or Go-like run exit-status mapping. External-driver
SIGTERM supervision is also unchanged: the verified orphan compiler, three
fixed `.new` stages, and later persistent-request poisoning remain open.
An existing local directory whose requested build basename ends `.ww`
(including a visible `_test.ww` symlink to a directory) remains a directory
package, not a raw named test source. WWstage `ww build` now uses the same