WSC-MVC — Technical and Product Specification
Version: 0.1 (development specification)
Status: Proposed; implementation behavior must be verified on the target Windows/IIS host.
1. Mission
Build a small, maintainable, WSC-first MVC framework for Classic ASP. IIS receives HTTP traffic; a single ASP bootstrap dispatches requests to VBScript Windows Script Components registered as COM objects. Application behavior lives in WSC files; presentation lives in separate HTML templates. Ship verified increments rather than an elaborate untested framework.
2. Fixed decisions
- Runtime: Windows, IIS, Classic ASP, VBScript; no ASP.NET, PHP, Node.js, or JScript runtime dependency.
- Object architecture: registered
.wsc COM components; stable ProgIDs and CLSIDs.
- Presentation: separate
.html templates, served only through the rendering layer when containing placeholders or private content.
- Entry point: one
Default.asp, plus IIS rewrite configuration and optional Global.asa only if justified.
- IIS is a hard runtime requirement for HTTP integration; WSH-based component smoke tests are a separate concern.
- Prefer built-in Windows/IIS capabilities and ADODB; no third-party framework required.
- Business logic must not reference ASP Request, Response, Server, or Session.
- All behavior dependent on WSC/COM/IIS quirks must be tested on Windows and documented; do not infer support from ordinary VBScript class behavior.
3. Non-goals for v0.1
No ORM, controller auto-discovery, automatic reflection, hot reload, plugin system, migrations, authentication, session-backed COM instances, application-scoped WSC instances, database-backed example, complex DI, or production-ready HTML parser. Do not implement these speculatively.
4. Initial execution path
GET /hello -> IIS URL Rewrite -> /Default.asp -> Server.CreateObject("WscMvc.Application") -> Application.Run(...) -> WscMvc.HomeController -> string/response contract -> HTTP 200, text/html; charset=utf-8, body Hello from WSC-MVC!.
Prefer a bootstrap with Option Explicit, no business logic, no includes, no embedded HTML, and only request-host wiring. Define and verify a precise ownership rule for HTTP status, headers, and writing: do not write a partially successful response before dispatch can fail. Test whether COM calls can accept the proposed ASP built-in objects; if passing an object fails, use a tested host adapter or pass only primitive request data. Do not silently assume an ASP object is serializable or universally callable from WSC.
5. Target project structure
WSC-MVC/
Default.asp
web.config
Framework/
Application.wsc
Router.wsc # phase 3
RequestContext.wsc # phase 2; only after host interop proven
ResponseResult.wsc # phase 2; optional if simpler contract works
ViewRenderer.wsc # phase 4
Controllers/
HomeController.wsc
Views/
Home.html # phase 4
Layout.html # phase 4
tests/
Test-Components.vbs
Test-Http.ps1
tools/
Register-Components.ps1
Unregister-Components.ps1
docs/
ARCHITECTURE.md
DECISIONS.md
TEST-RESULTS.md
SPEC.md
IMPLEMENTATION_PLAN.md
AGENTS.md
CLAUDE.md
README.md
The tree is a target, not an instruction to create placeholder classes before their phase.
6. Component contract
- Each
.wsc represents one named, cohesive component. The .wsc XML <registration> establishes COM identity; <public> explicitly lists callable methods/properties; <script language="VBScript"> contains implementation.
- Do not assume a VBScript
Class ... End Class placed inside a WSC becomes its exposed COM object. Use and validate the WSC script/public syntax on the target host.
- Use PascalCase for file names and public members,
WscMvc.ComponentName ProgIDs, and a unique, stable GUID per component (never recycle across unrelated components).
- A component has a minimal documented public API. Unexposed functions remain internal.
Initialize(...) is a framework convention, not an automatic WSC constructor.
- Use explicit parameter and return contracts, including whether values are objects (
Set) or scalars, and how missing/Null/Empty values are handled.
- Use composition; do not invent inheritance, decorators, interfaces, or reflection that VBScript/WSC cannot provide.
- Limit cross-component COM calls inside hot loops; keep interface calls coarse enough to measure.
- Do not store ASP host objects or mutable request data in persistent scopes.
7. HTTP and routing contract (phase 3)
- Support GET and POST initially; distinguish unsupported methods with a deliberate HTTP status.
- Normalize path consistently; preserve an explicit original path; match literal routes before parameterized routes.
- URL-decode exactly once where appropriate; validate route values and reject ambiguous or malformed paths rather than guessing.
- Static assets bypass the rewrite. WSC, config, source, tests, logs, and private templates must not be served publicly.
- Return deliberate 404 for unmatched routes, 405 for disallowed methods (with Allow where possible), 400 for malformed requests, 500 for unexpected failures.
- Don't use unvalidated request values as filesystem paths, ProgIDs, method names, SQL identifiers, or template names.
- Do not implement arbitrary controller/method activation from a URL; use an explicit allowlisted route map.
8. View contract (phase 4)
- Templates live separately from code and are not directly browser-accessible if they contain private information or placeholders.
- Default-encode data for HTML text; define separate, explicit handling for attributes, URLs, JS contexts, and raw HTML. Avoid unsafe universal substitution.
- Never execute VBScript or arbitrary expression code supplied by a template. Prefer a minimal documented placeholder syntax.
- Use a deliberate layout convention and report missing templates as server errors without exposing local filesystem paths.
9. Database contract (later phase)
- ADODB with provider-specific adapters only when needed; parameterized commands for values, never string-concatenate untrusted SQL data.
- Parameterize with correct order/type/size for the provider; identifier selection must use an allowlist.
- Connection, recordset, command, and transaction ownership must be explicit. Release objects and close resources on success and failure.
- Services/repositories do not depend on ASP host objects. Transactions have a clear owner and rollback path.
10. Security and operations
- Default deny direct HTTP access to
.wsc, .vbs, .ps1, .md, .config where appropriate, source/private directories, test output, logs, and templates. IIS configuration must be tested, including direct-extension and path variations.
- Avoid printing debug stacks, COM registration details, connection strings, paths, secrets, or raw exception text to clients.
- Record request correlation ID, route, elapsed time, outcome, and safe diagnostic information without sensitive payloads.
- No broad filesystem write privileges. Register WSCs from an elevated, controlled deployment step; do not let a web request register COM classes.
- Validate application pool identity, architecture (32/64-bit), DCOM/COM permissions where applicable, and IIS/ASP feature installation.
- Do not cache COM objects in Session/Application. Favor short-lived request-owned instances and document threading implications.
- Changes to COM registration must be idempotent, reversible, and explicit about privilege and architecture.
web.config is not a substitute for NTFS permissions or placing private source outside webroot; verify both where practical.
11. Error-handling contract
- Local
On Error Resume Next only around an operation whose Err.Number is immediately checked and cleared; otherwise use On Error GoTo 0.
- Handle errors at the nearest layer that can add useful context or recover. Central HTTP boundary emits a safe 500 response if headers/body are not committed.
- Define a testable strategy for already-committed responses. Never pretend an exception can undo a sent body.
- Distinguish an expected not-found/validation result from a programmer/COM failure.
12. Definition of done for each component
- Contract and source file exist; XML is well-formed.
- Public declarations match implemented functions/procedures and documented names.
- ProgID/CLSID identities are recorded and stable.
- Registration/unregistration is tested on Windows at the required bitness.
- WSH smoke test instantiates it where applicable; IIS HTTP integration runs when host objects are required.
- Failure path and cleanup are tested; no unmanaged mutable shared request state.
- Security review covers direct HTTP exposure and untrusted input.
- README or architecture docs reflect actual implementation, not intentions.
- Tests are reported as PASS, FAIL, or NOT RUN with commands, host details, and evidence.
13. Milestone sequence and gates
M0: Inspect repository/environment; record OS/IIS/ASP/bitness/permissions and missing tools. No claim of execution on Linux/macOS.
M1: Application.wsc and HomeController.wsc register and instantiate; /hello returns expected 200/body; isolated WSH test where possible; registration is reversible.
M2: Request/response context, central error mapping, per-request object lifetime; concurrent request smoke test.
M3: Static route table, GET/POST method dispatch, 400/404/405 tests, safe URL handling.
M4: Separate HTML templates and encoding tests; layout only after basic rendering passes.
M5: ADODB connection lifecycle, parameterized queries, transactions, repository tests against an explicitly configured test DB.
M6: Security, session/auth architecture, logging, deployment and concurrency hardening.
M7: Scaffolding, repeatable tests, documentation, agent-guided quality improvements.
Each milestone needs passing evidence before the next. Explicitly document any blocked milestone.
14. Acceptance tests for M1
cscript //nologo tests\Test-Components.vbs reports successful creation and expected method result (if the component's public API is host-independent).
powershell -File tests\Test-Http.ps1 -BaseUrl http://localhost:<port> verifies status, content type, and exact body for /hello.
- Direct requests to WSC files and private directories are denied.
- Missing ProgID or broken registration produces a safe 500; no filesystem path or raw internal exception is exposed.
- Unregistration removes the project registrations only and is reversible by re-registration.
- Test at least two concurrent HTTP requests; no cross-request leakage.
- No tests are described as passed without actual output.
15. Open questions to resolve experimentally
- Can the proposed WSC Application COM method receive ASP intrinsic objects across this specific IIS/COM boundary reliably? If not, use a small ASP-host adapter and primitive arguments.
- What exact registration tool and script-component XML forms work on the deployment OS and the relevant 32/64-bit host?
- What is the safe reload/deployment procedure for an updated WSC under load? Do not promise hot reload before measuring it.
- Which response contract best preserves error handling before bytes are sent?
Record experiments, chosen behavior, tradeoffs, and evidence in
docs/DECISIONS.md.
16. Reference reading