Repository navigation
Root applies to every surface, and the rename plan says what is stale about it - #717
Merged
Merged
Conversation
added 2 commits
August 27, 2026 14:04
…ing is written down Three corrections, all surfaced by ui becoming the first RoleModule|RoleAction|RoleRoot provider (#708) and making differences observable that had never been exercised. RootProviders described a filter that does not exist Its doc said callers filter the returned slice by dispatch zone and named plan.Provider as the example that "filters to RoleAction". plan.Provider does not filter -- buildPromotedBuiltins passes registry.RootProviders() through untouched, so every method of every root provider is promoted into plan.*. The code is correct; the comment described an intent never implemented. Corrected, with a note on why nobody noticed: flow is the only other root provider and it is surface=graph, so the distinction had no observable consequence until ui arrived holding both dispatch bits. RoleRoot now states its intent so it cannot be lost One bit, every surface, deliberately. Placement is orthogonal to dispatch, so a provider holding both dispatch bits surfaces flat in both namespaces from a single +devlore:root=true: note() in a script AND plan.note() in a graph. There is no way to be root in one zone and nested in the other, and nothing a provider could mean by asking -- a name is promoted because it reads better without its qualifier, and that is as true of a graph as of a script. The surface-to-role mapping existed only in generate.star As a switch, with no prose anywhere. The provider-development guide's directive table listed lifetime and bind and omitted BOTH surface and root. Now in the guide and in 3.6-method-classification.md: absent (default) RoleModule|RoleAction + root -> |RoleRoot graph RoleAction + root -> |RoleRoot module RoleModule + root -> |RoleRoot Both documents also carry the root-applies-everywhere rule, so a reader arriving at either one gets it. Related: #715 renames these roles, because surface=graph produces RoleAction -- two vocabularies for one idea -- and the op->workflow rename retires both words.
… lacks The plan presented 2026-05-26 measurements as current and claimed a prerequisite that has been satisfied for months. A reader arriving at it got stale facts with no sign that a re-audit was chartered -- every finding lived in issues the plan did not reference. A banner up front: prerequisite satisfied, numbers stale, do not start a phase from them, with pointers to #716 and #715. Prerequisites corrected in place, with the old blocker struck through rather than deleted. It was true when written, and the record should show that. Staleness (measured 2026-08-27), beside the Current State it corrects: root .go files 62 -> 108, provider files 76 -> 238, qualified op.X references ~2,499 -> 3,880. The subpackage list is wrong in both directions -- sops/ has left, claimcheck/ and server/ arrived. What still holds: all five renames are well-formed and the collision claim survives, Step being declared only in internal/console. New evidence FOR the target, also recorded: plan.Provider now exposes AssembleDefinition, LoadDefinition, and SaveDefinition, all returning *op.Graph, and scripts read plan.assemble_definition(...). The API already calls a graph a definition while the Go type is still Graph. The rename closes a gap that widened on its own. Appendix: Name Mapping -- TO BE WRITTEN. The Rename Mapping section is five rows, the top-level types, and nothing maps the derived identifiers -- 53 exported identifiers contain Graph, 37 contain Subgraph, 18 contain RecoveryStack, and 14 files carry a renamed word in their name. Goal 4 promises reviewers can verify by inspection; without this appendix there is nothing to inspect against. The appendix section works through why it needs judgment rather than derivation. Node is ambiguous by construction: it maps to Step AND is the target of ExecutableUnit, so it appears on both sides. GenerateNodeID is the proof -- flow/helpers.go calls it as GenerateNodeID(string(Subgraph)), so it mints ids for subgraphs and names the ABSTRACT VERTEX, which under the new taxonomy keeps the name Node. A mechanical Node -> Step sweep would rename it GenerateStepID and quietly make it wrong. And the requirement that identifiers KEEPING their name be listed explicitly: a table of only the changes cannot distinguish "correctly unchanged" from "missed". Related Rename: the provider roles, pointing at #715. Status stays draft. Leaving draft is #716's last acceptance item, and should not happen while the numbers are annotated rather than re-measured.
This was referenced Aug 28, 2026
David-Noble-at-work
added a commit
that referenced
this pull request
Aug 28, 2026
…alue (#726) Closes #715. +devlore:surface=graph produced RoleAction and +devlore:surface=module produced RoleModule -- two vocabularies for one idea, connected only by a switch in generate.star. An author wrote graph and the generated code said Action. The op->workflow rename forces the issue: Graph becomes Definition, so surface=graph would name a type that no longer exists, and module is starlark's word for a namespace rather than a term in the new taxonomy. Neither word survives. +devlore:surface=script|workflow a list; default both +devlore:placement=promoted one value; default qualified type ProviderFlags uint16 packs both type Surfaces uint8 BIT FIELD -- SurfaceScript, SurfaceWorkflow type Placement uint8 VALUE -- PlacementQualified, PlacementPromoted The zones are not the same kind of thing, and the design says so three ways PLURAL for the set, singular for the value. flags.Surfaces() returning SurfaceScript|SurfaceWorkflow reads correctly; flags.Surface() returning two things would read as a contradiction. The accessor signatures alone say which zone is which. DISTINCT TYPES per zone, following reflect, where flag.kind() returns Kind rather than another flag. flags.Placement() == SurfaceScript is now a compile error rather than nonsense that compiles. C# disagrees -- MethodAttributes packs a MemberAccessMask zone and flag bits in one enum -- and Go's answer was taken for the type safety. PlacementQualified = 0x00 is the zero VALUE of an enumeration, not an absent flag, so nothing can express "promoted and qualified" -- not because a check rejects it, but because the zone holds one value of a type with two. Why ProviderFlags rather than something specific ProviderRole was specific and went stale: #677 changed the zone from invocation mode to surface eligibility, and RoleAction, Dispatch(), and ProviderRole all outlived what they named. Dispatch() -> Surfaces() for the same reason. A generic name cannot fail that way, and the accessors carry the meaning. Precedent: 14 *Flag/*Flags types in Go's stdlib, and C#'s BindingFlags. The bootstrap had no two-pass escape generate.star emits the constant names into every .gen.go, so renaming them broke all 58 generated references at once, the tree stopped compiling, star could not rebuild, and codegen -- the only thing that could rewrite those files -- could not run. Unlike #708 no ordering avoids it: the constant name and its generated use must change together. make star-lkg first, so a prebuilt star could regenerate a tree it could not itself compile. It took five iterations of generate.star scope errors to get through, and the tree was uncompilable during all of them; each would have been unrecoverable without it. The LKG was then removed and a source-built star reproduced all 159 files identically, proving it was an escape hatch rather than load-bearing. A property that was documented three times and tested zero times #717 established that ui, reaching both surfaces, promotes six names into plan.* as well as installing six globals. That claim lived in three doc comments and no test -- and it is the half of placement this rename routes through new code. TestProvider_ResolveAttr_Tier2_PromotedAcrossBothSurfaces now asserts it, with a negative case proving file.copy stays qualified. Without the negative half it would pass on a change that promoted everything. Also: promoteRootMethods -> promoteMethods and rootNames -> promotedNames, resolving an identifier that carried two words for one idea. Placement test functions renamed; fsroot, run-root, scoped-root, and git-root names deliberately untouched -- a word-level sweep over "root" would break their meaning without breaking the build (#725 tracks the last two artifacts). The accessors mask explicitly rather than relying on the conversion to truncate, which gosec G115 caught: the bare form is correct only while both zones are a byte wide. Co-authored-by: David Noble <[email protected]>
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 free
to 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.
Documentation only. Two commits, independently reviewable.
1. Three corrections in
pkg/op, all surfaced byuiuibecame the firstRoleModule|RoleAction|RoleRootprovider in #708, which made three differencesobservable that had never been exercised.
RootProvidersdescribed a filter that does not exist. Its doc said callers filter the returned slice bydispatch zone, and named
plan.Provideras the example that "filters to RoleAction". It does not —buildPromotedBuiltinspassesregistry.RootProviders()through untouched, so every method of every rootprovider is promoted into
plan.*. The code is correct; the comment described an intent never implemented.Why nobody noticed:
flowis the only other root provider and it issurface=graph, so it holds one dispatchbit. The distinction between "root everywhere" and "root on the action surface" had no observable consequence
until a provider held both.
RoleRootnow states its intent. One bit, every surface, deliberately. A provider holding both dispatchbits surfaces flat in both namespaces from a single
+devlore:root=true—note()in a script andplan.note()in a graph. There is no way to be root in one zone and nested in the other, and nothing aprovider could mean by asking: a name is promoted because it reads better without its qualifier, and that is
as true of a graph as of a script.
The surface-to-role mapping existed only in
generate.star, as a switch, with no prose anywhere. Theprovider-development guide's directive table listed
lifetimeandbindand omitted bothsurfaceandroot.+devlore:surface=+devlore:root=trueRoleModule|RoleActionRoleModule|RoleAction|RoleRootgraphRoleActionRoleAction|RoleRootmoduleRoleModuleRoleModule|RoleRootNow in the guide and in
3.6-method-classification.md, both carrying the root-applies-everywhere rule, so areader arriving at either gets it.
2. The rename plan says what is stale about it
docs/plans/workflow-rename.mdpresented 2026-05-26 measurements as current and claimed a prerequisitesatisfied months ago. Every finding lived in issues the plan did not reference, so a reader arriving there got
stale facts with no sign a re-audit was chartered.
pkg/op/root.gofilesprovider/filesop.XreferencesThe subpackage list is wrong in both directions —
sops/has left,claimcheck/andserver/arrived.What still holds: all five renames are well-formed, and the collision claim survives (
Stepdeclared onlyin
internal/console).New evidence for the target:
plan.Providernow exposesAssembleDefinition,LoadDefinition, andSaveDefinition, all returning*op.Graph, and scripts readplan.assemble_definition(...). The API alreadycalls a graph a definition while the Go type is still
Graph. The rename closes a gap that widened on itsown, rather than imposing a new word.
Appendix: Name Mapping — TO BE WRITTEN. The Rename Mapping section is five rows of top-level types;
nothing maps the derived identifiers. 53 exported identifiers contain
Graph, 37 containSubgraph, 18contain
RecoveryStack, and 14 files carry a renamed word in their name. Goal 4 promises reviewers can verifyby inspection — without this appendix there is nothing to inspect against.
The section works through why it needs judgment rather than derivation.
Nodeis ambiguous by construction:it maps to
Stepand is the target ofExecutableUnit.GenerateNodeIDis the proof —flow/helpers.gocalls it asGenerateNodeID(string(Subgraph)), so it mints ids for subgraphs and names theabstract vertex, which under the new taxonomy keeps the name
Node. A mechanicalNode → Stepsweepwould rename it
GenerateStepIDand quietly make it wrong.Status stays
draft. Leaving draft is #716's last acceptance item and should not happen while the numbers areannotated rather than re-measured.
Tracked
surface=graphproducesRoleAction, and the rename retires both wordsVerified
make check,make test-race, andscripts/Test-GuideFrontmatter.shall pass.