Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,36 @@ auto-generated per-PR notes; this file is the curated, human-readable history.
template, concatenation, conditional, or otherwise) is an unconditional
violation. No product/runtime behavior changed — this hardens the
architecture gate's own soundness, not the policy it enforces.
- **#643: `tests/unit/side-panel-source-contract.test.ts` and
`tests/unit/surface-lifecycle-arch.test.ts` now use real TypeScript syntax
instead of unsafe comment preprocessing.** Both suites used to run a
two-pass regex comment stripper (block comments removed before line
comments) ahead of their own textual assertions — unsound in the direction
that matters most for an architecture guard: a `/*`-shaped substring
sitting inside a real `//` comment could make the block-comment pass
consume real code through the next genuine `*/`, hiding a violation before
either suite's assertions ever ran. Both now call two new named analyzers
in `build/lib/check-legacy-owners.mjs` (`findSidePanelSourceContractViolations`/
`findSurfaceLifecycleSourceContractViolations`, backed by the same shared
real-TypeScript-parser infrastructure #630/#642 already use, extended with
an internal `withParsedSources` batch primitive so the whole-tree surface
scan shares one native parser process rather than spawning one per file)
and a new `.d.mts` plain-data declaration boundary. Several rules
deliberately gained precision along the way (documented in each rule's own
test comments): the exact-value panel-id/label checks (app-preferences.ts,
state.ts, app-shell.ts panel ids) no longer false-positive on a longer
literal merely containing a protected id (`"pick 'library' now"` stays
clean) while gaining multi-quote-style coverage for the actual protected
value, including a TYPE-position literal (`type Pref = 'library'`); the
`history`/`sidePanel.value` comparison rules now support both operand
orders; the surface suite's ordering scopes now also recognize
return-annotated function declarations (a real gap in the retired textual
opener) while explicitly preserving its accidental treatment of
parenthesized control-flow blocks (`if`/`for`/`while`/`switch`/`catch (e)`)
as independent ordering scopes; and `currentWorkspace = null ?? fallback`
is now deliberately treated as clean (a `??` introduces real
conditional/fallback semantics a bare null-equivalent write does not have).
No production `src/**` code, dependency, or runtime behavior changed.

## [0.7.3] - 2026-08-06

Expand Down
97 changes: 97 additions & 0 deletions build/lib/check-legacy-owners.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Issue #643 — the strict-`.ts` declaration boundary over
// `check-legacy-owners.mjs`'s two source-contract analyzers
// (`findSidePanelSourceContractViolations` /
// `findSurfaceLifecycleSourceContractViolations`), consumed by
// `tests/unit/side-panel-source-contract.test.ts` and
// `tests/unit/surface-lifecycle-arch.test.ts`. Deliberately declares ONLY
// plain-data APIs — no `SourceFile`, no compiler `Node`, no `SyntaxKind`, no
// untyped callback parameter ever crosses this boundary, so a strict `.ts`
// caller never needs `any`/`@ts-ignore`/`@ts-expect-error` to consume it.
// `npm run check:types` proves declaration resolution and caller
// conformance; it does NOT prove this file accurately models the runtime
// `.mjs` shape — the two test files' own synthetic-source assertions are
// what actually exercise the real exports and validate the returned DTOs.
//
// This file intentionally does not declare every export the `.mjs` module
// has (the #630/#642 legacy-owner/package helpers, `manifestDependencyFields`
// et al.) — only the #643 side-panel/surface-lifecycle surface strict `.ts`
// callers need. Every other consumer of this module stays plain `.js`/`.mjs`
// (checkJs:false), so this declaration file never needs to describe them.

/** The #587 AC5 side-panel source-contract rule codes
* `findSidePanelSourceContractViolations` may report. */
export type SidePanelRule =
| 'workbench-sidepanel-mention'
| 'workbench-history-compare'
| 'app-preferences-panel-id'
| 'state-panel-label'
| 'app-side-panel-comparison'
| 'app-shell-panel-def'
| 'app-shell-panel-id'
| 'app-shell-host-accessor'
| 'side-panels-type-alias';

/** The #590 invariant (k) surface-lifecycle source-contract rule codes
* `findSurfaceLifecycleSourceContractViolations` may report. */
export type SurfaceLifecycleRule =
| 'surface-protected-declaration'
| 'surface-teardown-call'
| 'surface-signal-write'
| 'surface-current-workspace-null'
| 'surface-retirement-ordering';

/** One reported source-contract violation — a plain, JSON-serializable DTO.
* `pos` is the offending AST node's own `getStart(sourceFile)` (or `0` for a
* whole-file "the required construct is entirely absent" finding, which
* names no single node): a stable, deterministic identity, never a
* line/column and never required in a user-facing diagnostic. */
export interface SourceContractViolation {
readonly rule: SidePanelRule | SurfaceLifecycleRule;
readonly filename: string;
readonly pos: number;
readonly detail: string;
}

/**
* The #587 AC5 side-panel source contract, real-TypeScript-parser-backed.
* `filename` selects which (if any) rule group applies; a `filename` this
* function does not recognize returns `[]` without parsing `source` at all.
* Callers may pass either a real guarded file's current contents (with its
* real repo-relative `filename`) or synthetic probe source under the SAME
* `filename` to exercise that file's specific rule(s) in isolation.
*/
export function findSidePanelSourceContractViolations(
source: string,
filename: string,
): SourceContractViolation[];

/** One (filename, raw source) entry in a surface-lifecycle batch — `source`
* is the file's complete, unmodified text (comments included; nothing is
* stripped or reconstructed before parsing). */
export interface SurfaceLifecycleSourceEntry {
readonly filename: string;
readonly source: string;
}

/** `appFile` must be one of `sources`' own `filename` values.
* `coordinatorStart`/`coordinatorEnd` are the raw byte offsets of the
* `#590-COORDINATOR-BEGIN`/`#590-COORDINATOR-END` marker comments in
* `appFile`'s OWN raw source (the caller locates them there directly — the
* markers are themselves `//` comments, so they intentionally stay outside
* this function's AST-based analysis). */
export interface SurfaceLifecycleOptions {
readonly appFile: string;
readonly coordinatorStart: number;
readonly coordinatorEnd: number;
}

/**
* The #590 invariant (k) surface-lifecycle source contract, real-
* TypeScript-parser-backed, over one shared parser batch for the complete
* `sources` set (never one parse per file). Throws if `options.appFile` is
* not among `sources`' filenames.
*/
export function findSurfaceLifecycleSourceContractViolations(
sources: readonly SurfaceLifecycleSourceEntry[],
options: SurfaceLifecycleOptions,
): SourceContractViolation[];
Loading