Uh oh!
There was an error while loading. Please reload this page.
Declare the triggers capability so the app's flows actually fire - #72
Merged
Conversation
`objectstack.config.ts` declared `requires: ['automation', 'hierarchy-security']`. `automation` gives the app a flow ENGINE; it registers no TRIGGER. Every flow in the app was therefore inert: the assignment fan-out (#33) never fanned out and the three reminder sweeps (#70) never swept. Measured on @objectstack/cli 17.2.0, `PORT=3117 pnpm start`: before: Plugins: 35 loaded Flows: 4 flow(s) 0 bound to triggers + one "declares a '<type>' trigger but is NOT bound" warning per flow after: Plugins: 39 loaded (RecordChangeTriggerPlugin, ScheduleTriggerPlugin, TimeRelativeTriggerPlugin, ApiTriggerPlugin) Flows: 4 flow(s) 4 bound to triggers (record_change, schedule, time_relative, api) no unbound warnings; boot diagnostics 9 -> 5 One token covers all four kinds: `triggers` is the only trigger entry in `PLATFORM_CAPABILITY_TOKENS`, and the CLI keys it to @objectstack/trigger-record-change plus extras for the schedule, time-relative and api plugins. No second declaration is needed. `validate`, `typecheck`, `test` and `build` all exited 0 with every flow unbound, so `test/trigger-capability.test.ts` pins the invariant: it goes red if `triggers` is dropped while any flow declares a trigger, and it re-derives its assumptions (token spelling, one-token coverage, the flow `type` vocabulary) from the platform's own tables rather than restating them. It is a labelled stopgap over an author-time platform gap, filed as objectstack-ai/objectstack#14153, and is meant to be deleted when that lands. Part of #68
os-warren
marked this pull request as ready for review
September 1, 2026 08:49
Uh oh!
There was an error while loading. Please reload this page.
os-warren added a commit
that referenced
this pull request
Sep 1, 2026
Keeps every non-designated row inside the fortnight while putting real values in the 7-to-14-day band, so the dashboard's nested >7d / >14d / >30d tiles read 6 / 3 / 2 rather than 3 / 3 / 2. Also corrects the fan-out comment after #72: the reason a seeded assignment does not fan out is the loader's own skipTriggers, not an unbound trigger. Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
os-warren added a commit
that referenced
this pull request
Sep 1, 2026
* Seed the demo: the product working on first boot `pnpm dev` on an empty database now opens on a running system rather than five empty grids — a three-level business-unit tree, thirteen people, a twenty-item role catalog across three position codes, thirty-one duties and six months of dispatched history. History is produced by the dispatcher's own planner (`planDispatch`) rather than by a second period walk, so every period key is the engine's spelling by construction and "standing duties hold zero tasks" is structurally impossible to violate rather than merely absent from the fixture. `last_update_at` is written by a second `mode: 'update'` seed pass, per #32 / PR #64 — an insert can never carry it, and without that pass the "Not moving" view is empty while the seed reports success. Fixes#7 Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Spread in-flight touch ages by how long a task has been open Keeps every non-designated row inside the fortnight while putting real values in the 7-to-14-day band, so the dashboard's nested >7d / >14d / >30d tiles read 6 / 3 / 2 rather than 3 / 3 / 2. Also corrects the fan-out comment after #72: the reason a seeded assignment does not fan out is the loader's own skipTriggers, not an unbound trigger. Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Fixes#68
objectstack.config.tsdeclaredrequires: ['automation', 'hierarchy-security'].automationgives the app a flow engine; it registers no trigger. Every flow in the app was inert — #33's assignment fan-out never fanned out, #70's three reminder sweeps never swept — while all four gates stayed green.Verified on a real boot
PORT=3117 pnpm start,@objectstack/cli17.2.0, both runs on this branch's tree.Before (at
e3d6c7f,requireswithouttriggers):After (at
52afd5c, this branch's head):Zero of four → four of four. No
NOT boundwarning survives; the four[Automation]boot diagnostics are gone (9 → 5), and the four trigger plugins now appear in the loaded list.One token covers all four kinds — no second declaration
The warning names
@objectstack/trigger-*plural, so this was worth establishing rather than assuming. It is a single declaration:triggersis the only trigger entry inPLATFORM_CAPABILITY_TOKENS(28 tokens; nothing else matches/trigger/i).@objectstack/trigger-record-changeplus threeextras—ScheduleTriggerPluginandTimeRelativeTriggerPlugin(both from@objectstack/trigger-schedule) andApiTriggerPlugin(from@objectstack/trigger-api).PLATFORM_CAPABILITY_PROVIDERS.triggers.editionisopenand the packages are already on disk transitively, so nothing needs installing.jobis inPLATFORM_ALWAYS_ON_CAPABILITIESand mounts whether or not it is named.The boot line confirms it empirically: one token,
(record_change, schedule, time_relative, api).The regression guard
test/trigger-capability.test.ts(6 tests) goes red iftriggersis ever dropped while any flow declares a trigger. It does not restate platform facts — it re-derives them each run from the platform's own tables, so it reports a change instead of going stale:requirescontainstriggersrequirescontainsautomationisKnownPlatformCapability('triggers')/trigger/itoken,edition: 'open'typeenum member is classifiedTrigger detection ORs the two routes the engine and
@objectstack/lintboth use — the flow'stype, or a start node carryingtriggerType/timeRelative/schedule. Thetypeset is stated as its complement (screen,autolaunchedare the non-trigger ones), so a future enum member demands the capability rather than being waved through.Ablation — the guard was proven to fail
Committed first, then mutated and measured in one shell invocation under a restoring
trap; the mutation was confirmed on disk by grep counts on both the injected and the removed text (1/0→ flipped) plus a non-emptygit diff --stat, not by the editor's exit code.Dropping
'triggers'fromrequires:Exactly one of the six failed, and the other 18 test files stayed green — which is the measured restatement of #68's complaint: the existing suite is blind to this, and
validatestill exits 0 while every flow is dark. No rebuild step is involved: vitest transforms the config andsrc/TypeScript directly, so there is nodist/to go stale between the mutation and the measurement.Why nothing caught this — filed upstream as objectstack-ai/objectstack#14153
The card asked whether the asymmetry is real. It is, and it is one predicate wide:
defineStackruns exactly two validators that readrequires:validateKnownCapabilities(typo guard) andvalidateHierarchyScopeCapability, which throws on a hierarchy scope declared without its capability. There is no third.@objectstack/lint'svalidate-flow-trigger-readiness.tsalready computesisAutoTriggered— and uses it for one finding only (flow-draft-status-ambiguous). Thestackit receives carriesrequiresright there. The two are never joined.And the unguarded case is the worse one: a hierarchy scope without its capability fails closed (visibility narrows — wrong but conservative, and someone notices missing rows). An unbound flow fails silent — the automation simply does not happen and nothing anywhere says so.
test/trigger-capability.test.tsis therefore labelled a stopgap in the house convention (test/flow-predicates.test.ts,test/metadata-bindings.test.ts): it points at #14153 and is written to be deleted when that lands, not maintained.Why the guard is static rather than the
getTriggerBindingAudit()boot check #68 suggestedMeasured, not assumed: a kernel built the way
test/dispatch-wiring.test.tsbuilds one (createStandaloneStack+AppPlugin+bootstrap()) mounts no capability plugins at all. Its own boot log saysINFO Info: Optional service not present: automation, sogetService('automation')yields nothing and there is no audit to read —requiresis resolved by the CLIservehost, not by the kernel. Mounting the trigger plugins by hand inside the test would assert the test's wiring rather than the config's, which is the test-side-bind false greentest/dispatch-wiring.test.tswas written to avoid. So the assertion is made where the fact lives — the declaration the host reads — and the real-boot half is evidenced above.Gates
All four green on
52afd5c(the head of this branch), tree clean, run after the final commit:validatestill prints the one expectedhierarchy-securitycapability-provider warning — AGENTS.md rule 7 says that warning is this repo's expected state. No new warning appeared fortriggers: its provider packages resolve.Files changed
objectstack.config.ts—triggersadded torequires, with a comment recording what it turns on, that one token covers all four kinds, and that omitting it is caught by nothing at author time.test/trigger-capability.test.ts— new.Out of scope, filed unassigned: #71 — two catalog action handlers (
duly_catalog_apply,duly_catalog_sync) are registered without adefineActiondeclaration, so ADR-0110 D3 refuses both at dispatch on every boot. Visible in the boot diagnostics above; not touched here.src/data/,src/flows/,src/objects/andAGENTS.mduntouched.Generated by Claude Code