Re-architect plugin scaffold surface (#157): thin, typesafe, no plugin-source copy#172
Conversation
…old surface Re-architecture spine for #157: plugin add emits ONLY userland glue (wiring entrypoint + config edits + user-owned sample stubs + dep-sourced prisma) via a typesafe factory/typed code model. Disease (string-copied plugin TS source) is deleted. Supersedes chore/plugin-167-harden / PR #170. Grounded in 4 read-only agent sweeps (hexagon+emit map, plugin-add mutation map, prisma, doctrine+gates). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Add the typesafe core scaffold surface under packages/plugin/src/scaffold, exported as @netscript/plugin/scaffold. Provides ScaffoldArtifact, a typed manifest spec + byte-identical scaffold.plugin.json renderer, centralized kebab pluginName parsing, a composition factory (createPluginScaffold) over an injected FileSystemPort, and the --context-json CLI runner. No abstract base, no string codegen, no casing module. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…ugin-source copy) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…in-source copy) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…lugin-source copy) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…ugin-source copy) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…n-source copy) Re-architect the auth plugin scaffolder onto the shared @netscript/plugin/scaffold composition surface, replacing the legacy 26-template full plugin-copy with a single userland barrel. plugin add auth now emits only auth/mod.ts (re-exporting the published @netscript/plugin-auth-core/contracts/v1 surface); the service, routes, streams, Aspire wiring, deno.json/config, and auth.prisma all resolve from the @netscript/plugin-auth dependency (D-NOCOPY, D-CONFIG-KEEP, D-PRISMA: databaseMigrationsAdded:false). Delete artifacts.ts, files.ts, and all src/scaffold/templates/**. Migrate the auth manifest spec verbatim into plugins/auth/src/scaffold/spec.ts as authManifestSpec and retire the central packages/plugin test_fixtures/manifest-specs.ts (each plugin now owns its byte-identity test in-package). Completes S2 across all five official plugins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
… (v2) Re-architect the scaffold surface into FULL UNIFICATION: one core-owned plugin command contract (Vite-style composition + typed seams, strong single-target defaults, no cross-package inheritance). research.md + plan.md rewritten; context-pack added. Decisions also locked into PR #172 body + issue #167. Mandatory: install/doctor/info/update/remove. Optional: add/generate <resource>. ONE typesafe ItemScaffolder. Full rename+namespace. Forward-only reconcile of v1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…ew) + DoD Fold the user's two maintainer-facing capabilities into the unified plan: - S8 `netscript plugin verify` — author-grade plugin doctor (deno doc --lint + publish --dry-run + manifest schema + arch:check + adapter-contract completeness → one "exactly what is wrong" report; reuses repo/Deno tools). - S9 `netscript plugin new <name>` — conforming starter-shape generator built on the SAME unified item generator (dogfood); skeleton passes verify+dry-run. Both are the marketplace AUTHOR side of #167 and reinforce "core owns the shape". Also records an explicit Definition of Done: skill-first+harness, Deno-native first, JSR-ready, doctrine/fitness as actual merge gates, zero dead/dup code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
|
@openhands-agent model=openrouter/minimax/minimax-m3 provider=openrouter output=pr-comment iterations=600 use harness You are the PLAN-EVAL evaluator (separate session) for the unified plugin-command-contract SKILLActivate these repo skills before evaluating (each applies):
What to readOn the checked-out PR branch:
How to judge (emit PASS or FAIL_PLAN)Apply
OutputWrite your verdict to |
OpenHands Agent — CompletedModel: openrouter/minimax/minimax-m3 Summary — PLAN-EVAL (feat/scaffold-surface-167, PR #172, issue #167, task #157)
SummaryEvaluated the plan v2 for the unified plugin-command-contract re-architecture against
Changes
The workflow's automatic commit hook will pick up the new Validation
Responses to review comments or issue comments when relevantPLAN-EVAL does not post PR review-thread replies; the workflow owns PR comments. The verdict will The 2
A second PLAN-EVAL cycle (or auto-accept per user override) can then issue Remaining risks
Outputs
Run: https://github.com/rickylabs/netscript/actions/runs/28345993415 |
…max-M3) Hard Plan-Gate trigger on PR #172 for the unified plugin-command-contract re-architecture (S1-S9). No implementation slice begins before PASS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…n + cli User grants explicit license to rethink/re-architect the internal design of packages/plugin and packages/cli (not just graft the contract onto existing structure). Widens latitude, not the bar — all doctrine/JSR/no-leak/cast/lock invariants still bind. PLAN-EVAL prompt updated to judge bold restructuring as legitimate/preferred. Re-dispatching the gate on the updated plan. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
|
@openhands-agent model=openrouter/minimax/minimax-m3 provider=openrouter output=pr-comment iterations=600 use harness You are the PLAN-EVAL evaluator (separate session) for the unified plugin-command-contract Scope note (user-granted re-architecture license, 2026-06-29): the plan is explicitly authorized SKILLActivate these repo skills before evaluating (each applies):
What to readOn the checked-out PR branch:
How to judge (emit PASS or FAIL_PLAN)Apply
OutputWrite your verdict to |
OpenHands Agent — CompletedModel: openrouter/minimax/minimax-m3 PLAN-EVAL Run Summary — NetScript PR #172 / issue #167 / task #157SummaryEvaluated Plan v2 (user-locked 2026-06-29 in commit Verdict: Verdict artifact
ChangesWrote one artifact (no source or plan edits — read-only evaluator role):
No source, no plan, no branch mutations. Working tree dirty only on ephemeral request files ValidationCross-referenced every plan claim against the live branch tree:
Plan-Gate boxes (
|
|
[PHASE: IMPL] [SLICE: S1] [STATUS: PUSHED] S1 core Scope
A4 Fix
Files Touched
GatesExisting package-level dry-run warnings remain: the already-approved DriftRecorded in Commit
|
S2 workers connector — implementation completeScope:
Install == add proof:
Files touched:
Workers {
".": "./mod.ts",
"./adapter-cli": "./cli.ts",
"./aspire": "./src/aspire/mod.ts",
"./cli": "./src/cli/composition/main.ts",
"./contracts": "./contracts/v1/mod.ts",
"./scaffold": "./scaffold.ts",
"./services": "./services/src/main.ts",
"./streams": "./streams/mod.ts",
"./streams/server": "./streams/server.ts",
"./worker": "./worker/mod.ts"
}S4 repoint list recorded in
Raw gate output: Commit:
Push:
|
|
S2 workers cast-free fixup pushed: removed the new adapter connector casts and confirmed no remaining non- |
S3a sagas connectorScope: converted Commit: Install == add proof: Files created:
Files deleted:
Files modified:
Final {
".": "./mod.ts",
"./adapter-cli": "./cli.ts",
"./public": "./src/public/mod.ts",
"./plugin": "./src/plugin/mod.ts",
"./cli": "./src/cli/mod.ts",
"./scaffold": "./scaffold.ts",
"./e2e": "./src/e2e/mod.ts",
"./aspire": "./src/aspire/mod.ts",
"./runtime": "./src/runtime/mod.ts",
"./contracts": "./contracts/v1/mod.ts",
"./services": "./services/src/main.ts",
"./streams": "./streams/mod.ts",
"./streams/server": "./streams/server.ts"
}Gate tails: S4 drift list: no sagas-specific |
S3a triggers connectorScope: converted Commit: Install == add proof: Files created:
Files deleted:
Files modified:
Final {
".": "./mod.ts",
"./adapter-cli": "./cli.ts",
"./aspire": "./src/aspire/mod.ts",
"./cli": "./src/cli/composition/main.ts",
"./public": "./src/public/mod.ts",
"./plugin": "./src/plugin/mod.ts",
"./runtime": "./src/runtime/mod.ts",
"./scaffold": "./scaffold.ts",
"./services": "./services/src/main.ts",
"./streams": "./streams/mod.ts",
"./streams/server": "./streams/server.ts"
}Gate tails: S4 drift list: no triggers-specific |
…ter (one ItemScaffolder; delete legacy scaffolders)
|
S3b streams connector slice Scope:
Install/add proof:
Files:
Final export map: {
".": "./mod.ts",
"./scaffold": "./scaffold.ts",
"./adapter-cli": "./cli.ts",
"./cli": "./src/cli/composition/main.ts",
"./e2e": "./src/e2e/mod.ts",
"./aspire": "./src/aspire/mod.ts"
}Gate tails: S4 drift additions:
Commit: |
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…ce discovery Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Scaffold Runtime FinalizationPR #172 is now functional against the full scaffold runtime bar. Commits
Runtime EvidenceCommand: deno task e2e:cli run scaffold.runtime --cleanup --format prettyRaw summary: Notable green gates:
Final Gates
DebtClosed |
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
|
Check-test finalization for PR #172 is complete. Root causes fixed:
Commits:
Validation:
|
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct
…nfig (#199) * docs(site): reconcile to merged #193/#183/#190 surface + fmt-guard config Brings the doc site current with the three just-merged PRs, and hardens the standalone docs project config so `deno fmt` can never again reflow Vento markdown or split its `{{ comp }}` tags. Content (12 pages): - #190 fresh-ui: document the AI/workspace primitive catalog (11 L2 primitives + command-palette/search) and the headless Combobox interactive namespace; extra `ui:add` examples. (customize-fresh-ui, capabilities/fresh-ui) - #193 triggers-oRPC truth: triggers serve a typed v1 oRPC contract for introspection/management/SSE; only the webhook ingress endpoint (POST /api/v1/webhooks/:triggerId) stays a raw HMAC-verifying route by design. Corrected every stale "raw Hono, not oRPC" claim. (triggers, capabilities/index, contracts, architecture, glossary, deploy, storefront/05-shipping-webhook) - #183/#172 two-tier plugin shape: `netscript plugin new <name>` emits a core engine package + thin connector; --kind feature|proxy (default proxy), --overwrite. (cli-reference, author-a-plugin, plugin-system) Config: - docs/site/deno.json gains an fmt block (proseWrap preserve; excludes **/*.md, **/*.mdx, **/*.vto, _site/, _cache/) and a lint block, so the authored Markdown/Vento surface is out of scope for `deno fmt`. Verified: `deno task build` green (306 files); `check:links` green (18456 internal links across 130 pages resolve). Docs-only; no framework source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct * chore(openhands): record run trace 28485224548-1 * docs(site): correct `plugin new` flag names (IMPL-EVAL #199 Domain 2 FAIL_FIX) The two-tier topology prose was correct, but the CLI flag spellings were invented. Ground truth (packages/cli/.../new/new-plugin-command.ts): - boolean `--feature` (default proxy) → kind: feature|proxy - boolean `--force` → overwrite - `--project-root <path>` There is no `--kind feature|proxy` flag and no `--overwrite` flag. Running `netscript plugin new billing --kind proxy` would fail. Corrected the two docs that showed the wrong flags: - cli-reference.md: example → `netscript plugin new billing` (proxy default); desc → `--feature` / `--force`. - how-to/author-a-plugin.md: example → `netscript plugin new notifier`; options prose → `--feature` (route-backed feature connector vs default proxy), `--force` (overwrite), `--project-root <path>`. Verified: `deno task build` green (306 files); `check:links` green (18456 internal links across 130 pages resolve). Docs-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gW4zfhMMQU6txC828ijct * chore(openhands): record run trace 28486223232-1 --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: OpenHands Bot <openhands@all-hands.dev>
Re-architect the plugin command surface (#157, reframes #167) — ONE core-owned plugin contract
Draft — re-architecture in progress. Scope escalated from "thin scaffold" to full unification
of the entire
netscript plugin <verb> <kind>machinery. Final verify (S7) gates merge-readiness.The problem
The plugin command surface grew three overlapping mechanisms for the same job:
PluginScaffolder→renderPlugin()stringifies the entire plugin's type-safesource (
services/,router,contracts,src/runtime,src/aspire,bin/) into userland —duplicating package source the plugin already ships as a dependency.
src/scaffolding/+add-*verbs — a string-concatenation item generator ([...].join('\n'))plus dead
.templatefiles.…backed by three forked, base-less item-scaffolder contracts (core
PluginItemScaffolder,WorkersItemScaffolder, inlineSagasItemScaffolder, a bareTriggerDefinitionScaffolder).A plugin is a dependency, not a generator. There must be one contract.
The locked architecture (user, 2026-06-29)
@netscript/plugin+@netscript/cliown the command logic and rule the shape — opinionated,strong single-target defaults — modeled on Vite's plugin architecture (proven composition), made
more restrictive because NetScript is single-target, not multi-framework. Core is never aware
of any specific plugin. The plugin package is the "NetScript adapter" / connector: it plugs
plugin-core's implementation into the ecosystem (services, setup, scaffold, commands) by supplying a
typed contract object + seams — composition, never cross-package inheritance (doctrine 03
forbids it).
MANDATORY commands (every plugin; shared logic in core, parameterized by plugin seams — full
lifecycle, day one):
install(rename ofadd)doctorinfoupdateremoveOPTIONAL commands (per-plugin specifics; core defines the contract/shape like an oRPC contract,
plugin implements):
add <resource>(wasadd-*→add job/add task/add saga/…),generate <resource>(wasgenerate-*→generate registry), pluslist/run/logs/… .Naming — full rename + namespace (breaking CLI surface, acceptable pre-1.0):
netscript plugin install <kind>·netscript <kind> add <resource>·netscript <kind> generate <resource>.One item generator. A single type-checked
ItemScaffolder<TInput>(type-checked stubsource-of-truth + typed identifier substitution — no string concat, no
.template) drives BOTHinstall(starter set) andadd <resource>(user id). Kills all three mechanisms.Slices (each its own commit, gated green before the next; forward-only — no force-push)
@netscript/plugin/adapter:NetScriptPlugincontract + seams, the ONEItemScaffolder, mandatory command logic (install/doctor/info/update/remove),PluginCommandRunner(fixesPluginCli.run()A4 violation),createPluginAdapterfactory, strong defaults. Deletes the wrong v1src/scaffold/*(forward reconcile).src/adapter/plugin.ts+resources/{job,task}on type-checked stubs;cli.ts/scaffold.ts~3 LOC; deletescaffolding/+cli/*-cli-backend.ts.resources:[]+ wiring barrel).add→install, canonical mandatory verbs, per-kind resource routing, deleterenderPlugin()full-source branch, keep all config wiring +copyPluginSchemasToRootDb.arch:checkoverpackages/plugin+ 5 plugins; wire jsr-audit/doc-lint +plugins:checkinto the matrix; negative e2e (no plugin TS source in userland) + positive e2e (install starter set andadd <resource>emit from the SAME scaffolder).netscript plugin verify— author-grade plugin doctor (thedeno publish --dry-run/deno doctoranalog). Composesdeno doc --lint+deno publish --dry-run+ manifest-schema validation +arch:check+ a NEW adapter-contract-completeness check into ONE structured "exactly what is wrong" report (reuses repo tools, no bespoke linters). Self-test: green over all 5 first-party plugins. Distinct from the consumerdoctor.netscript plugin new <name>— conforming starter-shape generator (thecreate-viteanalog), built on the SAME unified item generator (dogfood). Emits a skeleton that passesplugin verify+deno publish --dry-runwith zero edits. The marketplace author on-ramp.arch:check,plugins:check, scoped check/lint/fmt, test, doc-lint,publish:dry-run×6+,e2e:cli run scaffold.runtime --cleanup,plugin verify/plugin newe2e), dead-code sweep, adversarial review, PR body finalize → ready-for-merge.Maintainer-facing tooling (user, 2026-06-29) — the marketplace AUTHOR side
The contract that lets core own the shape also gives third-party authors a
create-vite-gradeon-ramp (
plugin new) and adeno publish --dry-run-grade verifier (plugin verify). The generatoremits the conforming shape; the verifier proves conformance — both reinforce "core owns the shape"
and both are built on the SAME core machinery (no new mechanism). See S8/S9 above.
Definition of done (the user's bar; every slice honors it)
use harness+ matching skills).@std/*,Deno.*, Web Platform, native toolchain (deno doc/publish/check/lint/fmt) before any local abstraction; no hand-rolled linters/printers.jsr-audit(no slow types,@module+ symbol JSDoc +@example, clean publish list,publish:dry-rungreen).arch:checkextended overpackages/plugin+ 5 plugins;plugins:check)..templatefiles + v1src/scaffold/*deleted; S7 dead-code sweep proves it.Gates (the bar)
Scoped
check/lint/fmt(--ext ts,tsx), unit tests incl.scaffold.plugin.jsonbyte-identity,deno publish --dry-run(no slow types),arch:check,plugins:check, JSR doc-lint over each fullexport set, and
scaffold.runtimeE2E. Nodeno.lockchurn; no new casts beyond the 2 sanctioned;no
any. Definition of done + output quality are the user's bar.🤖 Generated with Claude Code