Uh oh!
There was an error while loading. Please reload this page.
fix(cli): os generate flow scaffolds a flow os validate accepts, and a test that keeps it that way - #14355
Conversation
The `flow` template emitted a top-level `trigger: { type, object, events }`
block, nodes carrying `name`/`next`, and no `edges` — four refusals against
`FlowSchema`, which is `.strict()` and binds a record-change flow on the START
node's `config` (`{ objectName, triggerType, condition }`), where
`AutomationEngine.resolveTriggerBinding` reads it from.
The template now writes that shape. `generate-scaffold-validates.test.ts` puts
every generator's output through the two steps `os validate` runs — schema
parse, then the author-time rule registry — loaded through the same
`bundle-require` path `loadConfig` uses, since a node `config` is an open slot
(ADR-0018) and the schema alone cannot judge the `record-*` trigger grammar.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza…ow-scaffold-validates
) The four `KNOWN_UNVALIDATED_SCAFFOLDS` entries were measured while implementing this card and filed as #14336; the ledger now says so, so whoever repairs one of those templates has a route from the entry to the card rather than only to the failure. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
📓 Docs Drift CheckThis PR changes 1 package(s): 12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ced9a1ea7804a217c60bb29584d4759426ab060e && git checkout ced9a1ea7804a217c60bb29584d4759426ab060e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e854a531abc9ee81264a17d0e0b1f41b38f6f03e c19eb733102eae66df9b6630e330631caebd3c5b && git checkout -B drift-repro e854a531abc9ee81264a17d0e0b1f41b38f6f03e && git merge --no-ff c19eb733102eae66df9b6630e330631caebd3c5b
node scripts/docs-audit/affected-docs.mjs --json e854a531abc9ee81264a17d0e0b1f41b38f6f03e
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#14087
The defect, reproduced before anything was edited
os g flow my_flowwrote a fileos validaterefused. Measured onorigin/maind63c8a2, scaffolded for the nameprobe_thingand put through the same stepsValidate.run()performs — four refusals in one parse, verbatim:Both halves of the card hold, and the measurement adds a third the card did not name:
edgesis a REQUIRED key onFlowSchemaand the template emitted none. The scaffold also declared one node and pointed itsnextat a node it never wrote.The fix
packages/cli/src/commands/generate.ts, theflowtemplate only. The trigger binding moves to whereAutomationEngine.resolveTriggerBindingactually reads it — the START node'sconfig:The card's spelling of the vocabulary was checked rather than copied:
record-after-writeis real (it isVALID_RECORD_TRIGGER's grammar inpackages/lint/src/validate-flow-trigger-readiness.ts, and'write'is the create-OR-update token from #3427), whileevents: ['after_insert', …]exists nowhere on the current surface.typemoves from'autolaunched'to'record_change'because that is what the retiredtrigger.typeblock declared this flow to be, and it is the declaration rule 1f gates.statusdeliberately stays'draft'. Arming is the author's decision andos validatesays so in an advisory; changing it would move the runtime behaviour of every generated app, which is outside what this card claims.Measured on the fixed scaffold: 0 error-severity findings, 2 advisories —
flow-trigger-unknown-object(theobjectNameplaceholder is not an object the fresh stack defines — the prompt to replace it) andflow-draft-status-ambiguous(thestatusdecision above).os validateexits 0.The scaffold-validates test
packages/cli/test/generate-scaffold-validates.test.ts. It loads each scaffold the wayos validateloads authored TypeScript — throughbundle-requirewithBUNDLE_REQUIRE_EXTERNALS, the same callloadConfigmakes — and then re-runs the two stepsValidate.run()performs on the result:normalizeStackInput+ the unknown-key lints +ObjectStackDefinitionSchema.safeParse, thenrunAuthoringRules('validate')gating on the error half.Both layers are load-bearing, and the second is the half a schema-only assertion would miss: a flow node's
configis an OPEN slot by design (ADR-0018), soFlowSchemacannot judge the trigger vocabulary at all. A start node carryingtriggerType: 'record_change'parses green and is caught one layer later byvalidate-flow-trigger-readiness. Asserting the schema alone would let this scaffold's own trigger token drift back to a spelling that never fires.Nothing in the file is restated: the roster comes from
GENERATOR_SCAFFOLD_TARGETS(built fromGENERATORSitself, now carrying each entry'sgenerate), and the stack collection each artifact lands in comes fromsingularToPlural— the mapdefineStackand the metadata registry already share. A generator added tomorrow is measured on the day it lands.Red-first, then ablated
Tests 2 failed | 12 passed (14), failing with exactly the four refusals quoted above.Tests 14 passed (14).label: 'Start'mutated toname: 'Start'. Mutation confirmed on disk in both directions (injected 1, original 0) and by blob hash (3dae1933→fbd28caf); the mutated tree fails 2/14. Restored withgit checkout HEAD -- ABSOLUTE_PATH, proven bygit diff HEADempty plus the blob hash back at3dae1933; restored tree 14/14. No rebuild leg is owed on either side: the test resolves the template through a relative../src/…path vitest transforms from source, so thedist/staleness hazard does not apply to this pair. The script carried atrap … EXIT INT TERMrestore throughout.Out of scope, filed rather than fixed
Running the derived roster is how it emerged that
flowis not the only refused scaffold. Same harness, same commit:objectparses and then failssecurity-owd-unset;view,actionandappfail the parse for reasons of their own;dashboardandskillare clean. Triage fenced that census out of this card, so it is #14336 and is not touched here.The four are recorded in the new test's
KNOWN_UNVALIDATED_SCAFFOLDS— shrink-only, in the shape this repo uses elsewhere, with an anti-staleness assertion: a kind IN the ledger must still FAIL, so whoever repairs one turns this test red and deletes its entry in the same PR. It can never grow to cover a regression, because a newly-broken kind is not in it and simply fails.flowis additionally asserted absent from the ledger, so this card's own defect cannot be re-admitted by adding a line to a table.#14337 is the other finding:
FlowSchema'striggeralias prescribes a rename totype, and taking that advice cannot work. That is the diagnostic a hand-writing author still meets; it lands inpackages/specand is not addressed here.The hot-file fence held — the migration codegen field-type
switchregion ofgenerate.tsis untouched.Verification
pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/generate-scaffold-validates.test.ts→ 14/14, onc19eb73310.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, 36 families) ran on that head. See the report comment onobjectstack generatescaffolds a flow thatobjectstack validaterejects — the trigger key and the events vocabulary do not exist on protocol 17 #14087 for the per-gate exit codes, including the two that report NOT MEASURED by their own exit-3 prerequisite branch rather than a verdict.pnpm lint(repo-wideeslint . --no-inline-config, no narrowing) → exit 0 in 1m54s.🤖 Generated with Claude Code
https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
Generated by Claude Code