a plugin can offer a commander, and offering is not starting
first half: the discovery and the two columns. no routes and no UI yet. PRESENCE IS THE DECLARATION. a plugin with commanders/<name>/COMMANDER.md offers one, and manifest.ts says nothing — same as tasks/ and skills/, and for the same reason: the directory already says it, and a second place to say it is a second place for the two to disagree. read in place through capabilitySources, so only an installed AND ENABLED plugin offers anything and plugins:update changes a template without republishing. A TEMPLATE IS NOT A COMMANDER. what a plugin ships is an OFFER: nothing runs, nothing is scheduled, no directory exists until an owner creates one from it. that is the shape asked for — a plugin may propose recurring work, only a person may start it. `plugin` IS A COLUMN, not a reserved category name. provenance should be structural: a category called "av-club" is a string an owner can rename, merge or delete, and the day they do, a plugin's commanders quietly become ordinary ones with nothing on disk saying otherwise. it also survives an uninstall, which is the point — the commander is still there and the UI can say where it came from. `plugin_config` holds the owner's answers because the form has to be RE-OPENABLE. somebody changing which directory is watched should see the one it is watching, not an empty form. the watcher script is generated from this, so this is the source of truth rather than the script. inputs reuse TASK.md's vocabulary exactly, so the existing form renderer draws them and the existing validation applies. "which folder, which extensions, what output" is the same question a task asks. `missingInputs` is the configure-before-running rule, and it is checked at ARM time rather than at creation. a half-configured commander that exists and is visible is useful; an ARMED one is what would run somebody's disk against a blank setting. columns verified live after db:push: plugin text, plugin_config jsonb. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
doublePrecision,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
serial,
|
||||
text,
|
||||
@@ -117,10 +118,7 @@ export const commandHqSessions = pgTable(
|
||||
* its own, and an array supports appending in place (`array_append`) without reading the whole column
|
||||
* back to modify it — `jsonb` would buy nothing here but a heavier read-modify-write for the same list.
|
||||
*/
|
||||
referencePaths: text('reference_paths')
|
||||
.array()
|
||||
.notNull()
|
||||
.default([]),
|
||||
referencePaths: text('reference_paths').array().notNull().default([]),
|
||||
/**
|
||||
* What the owner called it, held here rather than only in the transcript's summary record.
|
||||
*
|
||||
@@ -137,6 +135,29 @@ export const commandHqSessions = pgTable(
|
||||
categoryId: integer('category_id').references(() => commandHqCategories.id, { onDelete: 'set null' }),
|
||||
/** Should the platform keep re-arming this commander's watcher on every fire? See the file docstring. */
|
||||
watcherArmed: boolean('watcher_armed').notNull().default(false),
|
||||
|
||||
/**
|
||||
* The plugin that shipped this commander's template, or null for one somebody made themselves.
|
||||
*
|
||||
* A COLUMN rather than a reserved category name, for the reason tasks carry the same field:
|
||||
* provenance should be structural. A category called "av-club" is a string an owner can rename,
|
||||
* merge or delete, and the day they do, a plugin's commanders quietly become ordinary ones —
|
||||
* with nothing on disk saying otherwise.
|
||||
*
|
||||
* It also survives the plugin being uninstalled, which is the point: the commander is still there,
|
||||
* still in its directory, and the UI can say what it came from rather than pretending it was
|
||||
* always hand-made.
|
||||
*/
|
||||
plugin: text('plugin'),
|
||||
|
||||
/**
|
||||
* The owner's answers to the template's `inputs`, or null when there was no template.
|
||||
*
|
||||
* Held here rather than only in the generated watcher script because the form has to be
|
||||
* re-openable: somebody changing which directory is watched should see what it is watching now,
|
||||
* not an empty form. The script is generated FROM this, so this is the source of truth.
|
||||
*/
|
||||
pluginConfig: jsonb('plugin_config').$type<Record<string, string>>(),
|
||||
/**
|
||||
* The task id of the watcher currently believed live for this commander — set when armed (first arm,
|
||||
* and again on every automatic re-arm), read to correlate a `task:notification` back to "this is the
|
||||
|
||||
Reference in New Issue
Block a user