Skip to content

feat(front-end): build the API model from a package's source - #7

Open
systemfsoftware-maker wants to merge 102 commits into
mainfrom
feat/front-ends
Open

systemfsoftware-maker wants to merge 102 commits into
mainfrom
feat/front-ends

Conversation

@systemfsoftware-maker

@systemfsoftware-maker systemfsoftware-maker commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

FrontEnd.analyse turns a package's TypeScript source into the versioned ApiModel (schema version 2) and api-extractor's analysis messages. A package whose exports are all annotated takes the isolated-declarations fast path, with no type-check. Every other package goes through the TypeScript 7 compiler, and both routes give the same model.

This is PR3 of the six-PR plan in docs/plans/2026-10-10-1146-feat-effect-api-extractor-plan.md. It builds on the model, diff and verdict (PR1) and the toolchain (PR2). The .api.md report and differential lane (PR4), the CI benchmark (PR5), and the CLI and changeset-bump check (PR6) come later. The unit plan is docs/plans/2026-10-12-2048-feat-front-ends-plan.md.

Routing

flowchart LR
  P[readPackage] -->|no @systemfsoftware/source entry| N[NoSourceEntry]
  P --> I[IsolatedEmitter: oxc-transform]
  I -->|clean| B[buildModel + resolveMessages]
  I -->|NotIsolated: TS9xxx or unreadable entry| C[CompilerEmitter: tsc 7 child process]
  C -->|diagnostics| R[CompilerRejected]
  C --> B
Loading
  • One emitter port. DeclarationEmitter has two Context.Service tags. A TypeScript 7.1 API adapter can provide CompilerEmitter without changing the model schema. TypeScript 7.0.2's unstable/sync API has no declaration-emit request, so 7.0 uses the process adapter.
  • The emitter was chosen from a profile committed first. docs/profiles/2026-10-12-front-end-phases.md covers the 40 public systemfsoftware packages plus this one, which is isolated-clean. Both candidates agreed with tsc --isolatedDeclarations on every package. Emit plus parse took 77.79 ms with oxc-transform and oxc-parser 0.150.0, against 947.03 ms with TS 5.9.3 transpileDeclaration and createSourceFile. The fallback took 9257.41 ms. TS 7 and 5.9.3 printed 124 of 569 declaration files differently, so the builder reads structure and never depends on how one compiler prints. The commit adding the profile comes before the commit adding its Decision section. -front-end-phases-built.md repeats the run with the build and resolve phases.
  • Refusals are loud. UnsupportedDeclaration names the file and the construct: a named re-export from another package, declare global, module augmentation, export =, or an enum member with a computed value. One gap is documented in the README: export * from another package adds no items.

Review receipt (full path, exact head a98496e37468e7edebed5673423ad9bc2bf20fff)

ce-code-review mode:agent depth:full base:a053ee67129db1f9ba47a234ce8f06267c0b80b0 plan:docs/plans/2026-10-13-2019-feat-front-ends-plan.md. Run 20261013-202026-a98496e3, status complete, verdict Ready with fixes. metadata.json records head_sha a98496e37468e7edebed5673423ad9bc2bf20fff. Report-only: nothing below was applied. The 2c13063 receipt is superseded.

  • Reviewers. correctness, project-standards, testing, maintainability, performance, api-contract, reliability, security, adversarial and learnings-researcher all returned, plus a ci-wall-clock applicability reviewer. None timed out or failed.
  • Cross-model peer. openrouter/deepseek/deepseek-v4.1-flash at thinking max, read-only tools, no extensions. OpenRouter reports the served model as deepseek/deepseek-v4.1-flash. independence_verified: true (host Claude, peer DeepSeek). It contributed 3 findings. No Grok or xai-oauth route was used.
  • Packs. cell-architecture, boundary-testing, schema-laws, package-topology and ci-wall-clock at systemfsoftware@8d071180, with no resolver warnings or errors. The 50 enrolled rules come out as 27 comply, 5 violate and 18 not matched. The root-directed #761 rule complies and is listed on its own row.
  • Merge and validation. 20 candidates merged to 7 primary findings. 7 more were suppressed at the confidence-50 anchor; one of those (chore(packs): enroll ci-wall-clock and repin compound packs to 8d07118079 #8) went to the validator after I reproduced it against upstream. The validator confirmed 7 and rejected 1. No P0 survives; one confirmed P1 does, so the verdict is Ready with fixes.

Fixes since the 2c13063 receipt (U12, U13)

Each fix has a test that fails before it and passes after it. Rulings and scenarios are in the plan's U12 and U13 sections.

  • a98496e docs(front-end): rule the U13 delta review; record the node_modules mirror exception
  • 18c81e0 fix(front-end): mirror a package installed under node_modules without colliding with its own symlink (U13 delta)
  • 5831e97 docs(solutions): point the emit-option learning at the 1956 plan
  • a4ed6fd docs(front-end): rule the e5ec09d review (U13); third phase profile; mirrored node_modules learning
  • d77e2dd fix(front-end): a mapped-type key or an infer variable shadows a declaration of the same name (U13)
  • 432b608 fix(front-end): resolve types through the package's own node_modules ancestry; profile harness maps .cts and counts missing emits (U13 ci(mutation): run mutation testing on main through systemfsoftware's reusable workflow #5, ci(release): decide changeset coverage from turbo's build hashes #6)
  • c4202d9 fix(front-end): the fallback ignores the package's isolatedDeclarations; a diagnostic names a file only inside the package (U13 chore(release): version packages #4, feat(api-extractor): versioned API model, Effect-aware diff and semver verdict (PR1) #1)
  • c093a03 fix(front-end): skip null and array export targets; pattern exports read only TypeScript sources (U13 build: move api-extractor onto the systemfsoftware family toolchain #2, chore(packs): enroll ci-wall-clock and repin compound packs to 8d07118079 #8)
  • e5ec09d docs(solutions): point the emit-option learning at the 1834 plan
  • 35c33f4 docs(plan): correct D1 and D2 to what the adapters do; record delta rulings (U12)
  • 8efc442 docs(front-end): say both routes give one model apart from the producer stamp (U12 C11)
  • 1ba9588 fix(front-end): carry the package's root files and type roots into the derived config (U12 C3)
  • 7520eae fix(front-end): read compiler diagnostics from the TypeScript 7 API, not tsc stdout (U12 C3)
  • 3e81954 fix(front-end): decline what the fast path cannot prove (U12)
  • fa1f695 fix(front-end): read .cts entries and expand subpath pattern exports (U12)
  • 55eb06e fix(front-end): scope type parameters to their declaration in the resolver (U12)
  • 5a862ac fix(front-end): keep local alias names, optional tuple marks and setter text exact (U12)
  • 25d0ef9 fix(front-end): synthetic diagnostics carry no TypeScript code (U12 C4)
  • fb70da8 docs(plan): rule every open front-end finding (U12)

Findings at this head

# Sev Finding Location Reviewers Validation Disposition
1 P1 Anonymous default export is silently dropped from the model declaration-graph.ts:369 correctness confirmed Open for the root. Recommend accepting: declare an anonymous default function or class under default, plus a builder scenario. A patch exists, unapplied.
2 P2 Call, index and construct signature members never built in a test type-expr-of.ts:299 testing confirmed Open for the root. Recommend accepting: one builder scenario with an index, a call and a construct signature.
3 P3 TypeExprs exposes two members no caller destructures type-expr-of.ts:513 maintainability confirmed Open for the root. Recommend accepting: drop the two unused members.
4 P3 Profile harness copies the shipped declaration-path and tsconfig rules profile.ts:133 maintainability confirmed Open for the root. Recommend accepting: export declarationPathOf and the tsconfig choice to the harness, or rule the copies deliberate (KTD10).
5 P3 readPackage leaks Schema.SchemaError into its public error channel read-package.ts:182 api-contract confirmed Open for the root. Recommend accepting: map the manifest decode failure to a tagged ManifestRejected beside NoSourceEntry.
6 P3 Compiler fallback producer stamp never asserted typescript-7-compiler.ts:275 testing confirmed Open for the root. Recommend accepting: assert TYPESCRIPT_PRODUCER on a real fallback model.
7 P3 Resolver oracle never compares a message's item path analysis-messages.integration.test.ts:33 adversarial-deepseek rejected Rejected by the validator as a re-raise of A18. Orchestrator note below: A18 landed only in part.
8 P2 Release tag read from raw comment text, so a prose mention sets the tag resolve-messages.ts:28 adversarial-deepseek confirmed Open for the root. Recommend accepting: read release tags with the TSDoc parser upstream uses, or strip code spans and fences first, with oracle scenarios.

Evidence beyond the reviewers (public surface, TypeScript 7.0.2, upstream api-extractor 7.59.1):

Suppressed at the confidence-50 anchor:

  • Same-stem .ts/.tsx siblings collapse to one declaration path; only entries are guarded, so the two routes can model different files (adversarial, P2, oxc-isolated-emitter.ts:144)
  • Committed profile report bypasses the designated serialiser (project-standards, P3, profile.ts:439)
  • Front-end outside interaction is an Effect generator, not a Sandwich cell (learnings, P3, analyse.ts:38)
  • Unreadable source aborts the fast path instead of declining (reliability, P3, oxc-isolated-emitter.ts:51)
  • Foreign package.json export map interpreted by hand, not decoded (learnings, P3, read-package.ts:39)
  • Fallback refuses a package when a reached declaration file was not emitted (adversarial-deepseek, P3, typescript-7-compiler.ts:269)

Enrolled pack rules (all five packs, every rule)

Pack Rule Verdict Evidence
cell-architecture callable-vs-resource-syntax.md not matched No ADT/blueprint/handle is minted. The only new shape is a one-operation service (declaration-emitter.service.ts:6-8), whose taxonomy is ruled by service-and-layer-boundaries.
cell-architecture decode-never-cast.md comply No as T / as unknown as anywhere in src (only as const); package.json goes through Schema.decodeEffect(Schema.fromJsonString(PackageManifest)) (read-package.ts:15,187); tsc diagnostics are read from the TS API as typed values, not cast (`typesc
cell-architecture four-channel-contracts.md comply New E-channel members are Schema.TaggedErrors carrying data only (front-end-error.schema.ts:13-49); none wraps a caught exception, so none owes cause: Schema.optional(Schema.Unknown). Residual: catchCause swallows (see residual risks).
cell-architecture handle-state-privacy.md comply Neither emitter holds per-instance state; the layer captures Effect.context in a closure (oxc-isolated-emitter.ts:163-165, typescript-7-compiler.ts:376-379). No module-level WeakMap/let registry.
cell-architecture pipeable-dual-parity.md not matched No blueprint or handle is authored.
cell-architecture pipeline-composition.md not matched No cells are composed and no cell.run appears inside an Effect.gen.
cell-architecture pure-decision-workflows.md not matched No *.workflow.ts decision is added; judge-surfaces.workflow.ts:13 is a type annotation only.
cell-architecture resource-vs-handle-duality.md comply The emitters are stateless capability services, not lifecycle handles smuggled into ambient services (declaration-emitter.service.ts:6-23).
cell-architecture sandwich-phase-order.md violate The front-end's outside interaction is a bare Effect.gen: analyse.ts:38-50 (export const analyse = (source: PackageSource): Effect.Effect<Analysis, …> => Effect.gen(…)) and read-package.ts:182-193, against the rule's "Every outside interaction is an I/
cell-architecture scoped-lifecycle-boundaries.md comply Scoped temp dirs (makeTempDirectoryScoped, typescript-7-compiler.ts:114,147), the compile inside Effect.scoped (:361), and the TS API handle via Effect.acquireUseRelease with api.close() as release (:94-98, :226-230).
cell-architecture service-and-layer-boundaries.md comply Contract in *.service.ts with zero driver imports (declaration-emitter.service.ts:1-4); driver-backed layer exports live in adapter modules beside it (oxc-isolated-emitter.ts:160, typescript-7-compiler.ts:369); no *Live, no .port.ts/.layer.ts.
cell-architecture single-namespace-barrel.md comply mod.ts:1-3 exports three cohesive namespaces; FrontEnd.ts:1-25 is the one barrel for the capability — no error/schema fragment barrels.
cell-architecture staged-lawful-builders.md not matched No blueprint/builder for an external target; the syntactic model builder is not that shape.
cell-architecture store-serializable-unit-of-work.md not matched No store, read-modify-write or transaction in the diff.
cell-architecture store-unit-of-work-handle.md not matched Same — no store port or unit handle.
boundary-testing fake-and-real-store-laws.md not matched No store over shared state; no fake/adapter pair is added.
boundary-testing no-mocks-on-internal-glue.md comply No vi.fn/vi.mock/vi.spyOn anywhere in the package or the e2e app; every feature imports the public entry only (front-end-model.integration.test.ts:2, front-end-routing.integration.test.ts:2, package-reader.integration.test.ts:2, `analysis-messages.
boundary-testing pin-dependency-semantics.md comply The vendor assumption (oxc's isolated declarations agree with tsc) is pinned executably by the route-equality journeys over real packages (front-ends.integration.test.ts, throughBothRoutes) and by the committed per-candidate verdicts in `docs/profiles/20
boundary-testing port-fakes-pass-the-port-contract.md comply No fake exists for IsolatedEmitter/CompilerEmitter; both routes run their real adapters (e2e FrontEnd.isolatedEmitterLayer/compilerEmitterLayer; KTD11 "no emitter is faked").
boundary-testing real-system-oracles.md comply The compiler journey spawns the real tsc of this package's dependency on real temp packages (front-ends.integration.test.ts:1-6, live), the in-process features use real temp dirs/files, and refusal is proven (TS9010 routing, TS5033, missing config, exclu
boundary-testing staged-protocol-evidence.md comply The route is a tagged union built only from the isolated emitter's own evidence — `Route = FastPath \
schema-laws arbitrary-filter-floors.md comply The one custom filter in src carries its generation hint (api-model.schema.ts:98-102, used at :140 and :153); Diagnostic.code uses the built-in Schema.isGreaterThan on Schema.Int (declarations.schema.ts:44); no S.isPattern in src; the looka
schema-laws cross-field-invariants-as-struct-checks.md comply No new struct has a relation among its own fields, and no consumer compares two fields of a decoded value before trusting it.
schema-laws data-only-schema-classes.md comply The new classes are Schema.TaggedErrors (the one allowed case), hold data only, and define argument-less get message() getters reading their own fields (front-end-error.schema.ts:16-49, verdict.schema.ts:63-93); no instance methods, no non-metadata sta
schema-laws invariants-as-refinements.md comply Diagnostic.code is refined (Schema.Int.check(Schema.isGreaterThan(0)), declarations.schema.ts:44) instead of a bare Finite (U12 C4); no consumer re-checks a decoded value. Gap: no brand, no refusal property (root-directed row).
schema-laws law-failure-is-a-codec-defect.md not matched Base commit a053ee6 is "test(api-extractor): retire the generated schema laws (#9)"; there is no src/schema-laws.test.ts and no schema-laws plugin in vitest.config.ts:1-11.
schema-laws recursive-schema-suspend.md comply (pre-existing) Recursion points keep Schema.suspend + explicit Schema.Codec<T> and recursionBudget (api-model.schema.ts:113-116,145; type-expr.schema.ts:133-134,156-171,174-190); this diff changes only KeywordName's declaration form (type-expr.schema.ts:14-19).
schema-laws refusals-beside-generated-laws.md not matched Generated laws retired at base; ruling #761 re-scopes refusals to unobservable boundaries; no import.meta.vitest block and no refined codec from ruling #2 survives U12 C3.
schema-laws rich-type-over-foreign-encoded.md violate read-package.ts:39-40 (typeof exports === 'object' ? Object.entries(exports)…), :25-28 (Schema.is(ConditionMap), isString) interpret the foreign manifest's export map in application code while package-source.schema.ts:6-24 keeps only the raw Encode
schema-laws tagged-unions-over-state-by-presence.md violate (settled) declarations.schema.ts:42-46 makes file/code optional-by-provenance and consumers probe them (front-end-error.schema.ts:8,11); the sentinel design the rule targets (UNPARSED_FILED_LINE) is gone. A finding re-raising this shape is a non-finding per th
schema-laws tests-own-no-schemas.md comply No test declares, copies or aliases a schema; production schemas are reached through the package entry (front-end-model.integration.test.ts:2,6) and the only test-side shapes live in the harness fixture tests/__fixtures__/upstream-messages.ts:1-5. The `com
package-topology condition-branch-agreement.md comply The new namespace reaches every branch because all branches derive from the single src/mod.ts:1-3; exports['.'] in packages/api-extractor/package.json is unchanged by this diff.
package-topology declared-entry-points.md comply No subpath is added, renamed or removed; the exports map is untouched (only dependency moves in package.json), and ./package.json remains.
package-topology enumerated-entry-modules.md comply mod.ts:1-3 names its three namespaces; FrontEnd.ts:1-25 names every export it publishes (no export *), and it is pure surface — re-exports only, no definitions.
package-topology import-time-inertness.md comply Every entry module-scope value is a declaration: schemas, Context.Services, regexes, version stamps (oxc-isolated-emitter.ts:10, typescript-7-compiler.ts:11), import.meta.resolve (typescript-7-compiler.ts:13). No I/O, env read, clock, randomness, fib
package-topology one-access-path.md comply Each symbol is reachable once, inside its namespace, from the root only (mod.ts:1-3); no aliases, no flat+namespace duplicate; TYPESCRIPT_PRODUCER and OXC_TRANSFORM_PRODUCER are different symbols (FrontEnd.ts:13,24).
package-topology public-signature-types.md comply Every type the new published signatures reach is exported from FrontEnd.ts:1-25 — Analysis, Route/FastPath/Fallback, Declarations, DeclarationFile, Diagnostic, PackageSource, SourceEntryPoint, DeclarationEmitter, both ids, the four error
package-topology surface-changes-are-versioned.md comply The only declared-surface change is the added FrontEnd namespace → minor in .changeset/old-cobras-punch.md:1-2; wire versions are unchanged (API_MODEL_SCHEMA_VERSION = 2, VERDICT_SCHEMA_VERSION = 1), and U1's class→typed-constant moves keep both the
ci-wall-clock flake-outputs-build-once-on-main.md violate (pre-existing) .github/workflows/ci.yml:4 (pull_request),:22-40 (sfs-deps: timeout-minutes: 10 at :25; nix build .#sfs-deps at :29-30; consumer-store at :32; no cache step); .github/actions/checks-lane/action.yml:23,27; flake.nix:82-105 (sfsDepsOf linkFarm over the pinned
ci-wall-clock gates-share-one-job.md not matched .github/workflows/ci.yml:43-53 (checks job and lanes JSON, base-identical); .github/actions/checks-lane/action.yml:29-43; reusable-checks.yml @732f66a0:208-234 (lane matrix); empty .github diff base..head
ci-wall-clock heavy-lanes-state-their-daily-minutes.md not matched .github/workflows/ (ci.yml, release.yml, changeset-check.yml only; all base-identical); files.txt has no .github path; AGENTS.md:24 ('Mutation testing runs on main only')
ci-wall-clock heavy-suites-judge-every-main-commit.md not matched .github/workflows/ci.yml:3-8 (triggers),:13-15 (concurrency), all base-identical; empty .github diff; apps/api-extractor-e2e/tests/front-ends.integration.test.ts (added inside an existing package); no VM test or image build in the flake's checks (flake.nix:129
ci-wall-clock light-checks-run-the-pinned-tool.md not matched .changeset/old-cobras-punch.md (added, 'minor'); .github/workflows/changeset-check.yml:12-17 (base-identical, devshell: true at :17, pin at :13); callee pnpm-release-management@b9b4a1d .github/workflows/changeset-check.yml:55-84; empty .github diff
ci-wall-clock no-dev-shell-on-verdict-lanes.md not matched .github/actions/checks-lane/action.yml:20-27 (pinned tool fetches),:29-43 (artifact download + frozen install), no dev shell; README.md:113-115 (local reproduction only); empty .github diff
ci-wall-clock no-lane-grades-the-repos-own-docs.md comply packages/api-extractor/tests/front-end-model.integration.test.ts, front-end-routing.integration.test.ts, package-reader.integration.test.ts, analysis-messages.integration.test.ts; apps/api-extractor-e2e/tests/front-ends.integration.test.ts; packages/api-extrac
ci-wall-clock one-stuck-job-backstop.md violate (pre-existing) .github/workflows/ci.yml:4-5,22-40 (:25 timeout-minutes: 10),:43-53 (:50 lane timeout-minutes 10; :51 timeout-minutes: 10; :52 max-seconds: 600; :53 plan-timeout-minutes: 10),:66 (10),:91 (5); .github/workflows/release.yml:27-34 (:31 timeout-minutes: 1); calle
ci-wall-clock pr-gate-runs-only-what-the-diff-affects.md comply package.json:12-15; turbo.json:3-21; pnpm-workspace.yaml:2-3; .github/workflows/ci.yml:4 (pull_request),:43-53 (single reusable gate call, no paths filter, base-identical); flake.nix:129-136 (checks holds only consumer-store); .compound-engineering/config.yaml
ci-wall-clock red-main-reverts-its-culprit-same-day.md not matched empty .github diff base..head; .github/workflows/ (ci.yml, release.yml, changeset-check.yml only); no workflow_run trigger anywhere in .github
ci-wall-clock release-promotes-the-staging-proven-build.md not matched .github/workflows/release.yml:1-34 (base-identical; concurrency release-${github.ref} with cancel-in-progress: false at :12-14; reusable pin at :24; noop job at :27-34); .changeset/old-cobras-punch.md (added); nix eval packages.x86_64-linux.workspace-tarballs
ci-wall-clock setup-under-a-minute.md not matched .github/workflows/ci.yml:22-40 (base-identical),:32 (consumer-store build whose inputs the diff changes); .github/workflows/changeset-check.yml:17 (devshell: true, base-identical); nix eval pnpm-store/consumer-store yw6gq1b2 -> lkxq1vhh; empty .github diff bas
root-directed, not enrolled schema-laws/refusal-properties-cover-unobservable-boundaries.md (systemfsoftware 0923912e, #761) comply No refinement in the diff carries a refusal property; U11 removed the unobservable ones and the e2e refusal journey proves the observable boundary.

The violations:

  • cell-architecture/sandwich-phase-order (unsettled): analyse and readPackage are bare Effect.gen interactions, not Sandwich cells. The plan fixes a port and a namespace, not a cell shape.
  • schema-laws/rich-type-over-foreign-encoded (unsettled): read-package.ts interprets the foreign export map in code, and package-source.schema.ts keeps only the raw union.
  • schema-laws/tagged-unions-over-state-by-presence: settled by U13 feat(api-extractor): versioned API model, Effect-aware diff and semver verdict (PR1) #1 and U12 C4, which make Diagnostic.file and code optional.
  • ci-wall-clock/flake-outputs-build-once-on-main and one-stuck-job-backstop: pre-existing. .github, flake.nix and flake.lock are identical to base.

ce-compound

No new learning this round: no code changed. The existing docs/solutions/logic-errors/compiler-fallback-inherits-declaration-emit-options.md passes all three validators at this head, and the learnings reviewer cites it as honored.

Verification

  • Local, at a98496e37468e7edebed5673423ad9bc2bf20fff: pnpm turbo run lint typecheck build test --force gives 9/9 tasks. Package tests 148/148, e2e 4/4.
  • tsc -p tsconfig.build.json --noEmit is clean, so the package stays isolatedDeclarations-clean. attw reports ok.
  • The host digest is unchanged (e4478f03…6da2), so verdict output bytes are unchanged. git grep finds no locale APIs in src.
  • CI at a98496e37468e7edebed5673423ad9bc2bf20fff: CI run 38462248388 (11 jobs) success; Changeset Check run 38462248604 success.
  • PR1 counterexample, recorded and not re-run: CI run 38372869767 (head 2b2e595) refuted the generated law ∀x_TypeExprNesting_≤MaxDepth1 with replay property=3804450008;seed=2068186473;runs=3;attempt=2;size=10;path=2,1,1,3,3,5,5,4. The same replay failed on origin/main cd9f133d, and main retired the generated laws in test(api-extractor): retire the generated schema laws #9 (a053ee6).
  • Turbo already owns the build, cache and task graph. This unit adds no cache mechanism, and public CI carries no Turbo credentials.
  • No mutation run, by rule.

Dated unified plan for the isolated-declarations fast path, the TypeScript 7
compiler fallback behind one emitter port, the syntactic model builder and the
analysis-message resolver. Cites the U7 contract (origin R15/R17/R18,
KTD9/KTD11/KTD13) and the cell-architecture, boundary-testing, schema-laws and
package-topology packs; folds in the ce-doc-review findings.
…(U1)

tsconfig.build.json sets isolatedDeclarations, so the build fails when an
export loses its annotation (plan R15, KTD9). Exported schemas carry their
Schema types; the Verdict and CompareSurfaces schema classes become typed
constants beside their instance types (TS9021/TS9038), which keeps
effect-schema-discovery generating their codec laws. Verdict output bytes are
unchanged (host digest e4478f03...6da2).
apps/api-extractor-profile times discovery, oxc-transform and
transpileDeclaration isolated emit, TypeScript 7 and 5.9.3 tsc emit, and
oxc-parser and createSourceFile parsing for this repository's package and the
39 public systemfsoftware packages with a TypeScript source entry at
732f66a0c8, and records each candidate's isolated-clean verdict beside
tsc --isolatedDeclarations (plan R12, R13, KTD10). Committed before the
fast-path emitter is chosen.
…er (U3)

The committed profile picks oxc-transform 0.150.0 with oxc-parser by the
plan's pre-registered rule (KTD2): both candidates agree with
tsc --isolatedDeclarations on all 40 packages, and oxc emit plus parse takes
77.79 ms against transpileDeclaration's 947.03 ms. The Decision section of
docs/profiles/2026-10-12-front-end-phases.md records the numbers.

Adds the DeclarationEmitter port with IsolatedEmitter and CompilerEmitter
tags (no driver imports), the Declarations meeting point, the package reader
(NoSourceEntry for a package with no TypeScript source entry), the
declaration graph both emitters close over, and the isolated adapter that
refuses with NotIsolated and the emitter's diagnostics.
… 7 fallback and router (U4-U8)

- build-model, type-expr-of, canonical-type-text: one syntactic builder turns
  declaration text into the ApiModel v2 on both paths (KTD6, R6-R8); import()
  types and imported names give one reference; undecomposed types print through
  one canonical printer.
- resolve-messages: ae-forgotten-export, ae-missing-release-tag,
  ae-internal-missing-underscore, ae-incompatible-release-tags and
  ae-internal-mixed-release-tag from declaration text (KTD7, R9-R11), checked
  against upstream api-extractor 7.59.1 run in-process.
- typescript-7-compiler: the CompilerEmitter adapter runs this package's tsc 7
  as a process, decodes its output through lawful line codecs, and closes over
  the same declaration graph as the isolated adapter (KTD3, R2, R5).
- analyse and the FrontEnd namespace route FastPath or Fallback (KTD4, KTD8).
- The declaration graph treats a declaration module without export {} as
  exporting every top-level declaration, as TypeScript does.
- The isolated adapter declines with NotIsolated when oxc's output for a file
  that had diagnostics does not parse (a destructured export), instead of
  refusing the package.
- e2e journeys: fast path and fallback give this package the same bytes and
  messages (AE1), an inferred export routes to the fallback naming TS9010
  (AE2), a type error is refused with TS2322 (AE5).
analyse builds the declaration graph once and hands it to the builder and the
resolver; the closure walk pushes onto a queue instead of copying it per file;
compiler-output decoders are built once; one bindingName and one optional-type
printer; the analysis message ids are one const list; resolver de-duplication
uses Arr.dedupeWith. No behaviour change: 198 package tests, 4 e2e tests and
the host digest are unchanged.
…README front-end section (U9)

The harness now runs FrontEnd.readPackage, the routed emitters, buildModel and
resolveMessages on every corpus package and records the route and build
outcome. On systemfsoftware 732f66a0c8: 12 fast path, 28 fallback, 37 of 40
models built; declare global, a module augmentation and an external re-export
are refused with UnsupportedDeclaration. The first profile and its emitter
decision stay unchanged.
- Changeset: a minor intent for @systemfsoftware/api-extractor, because the FrontEnd
  namespace is new public surface (package-topology/surface-changes-are-versioned).
- Compiler-output codecs: Position is capped below 1e9 and digit text has one spelling
  (no zero padding). Each refinement has a refusal law in its own file
  (schema-laws/refusals-beside-generated-laws), and each law fails when its refinement is
  widened. The four committed Refuted seeds are kept as regression coverage, because they
  still express valid cases.
- The isolated emitter declines with NotIsolated, naming the declaration file, when an
  entry point's source cannot be read, instead of modelling an empty entry point.
- An entry point's declarations load from its recorded source path first, so a .tsx entry
  is no longer served from a sibling .ts.
- The TypeScript 7 fallback passes --stripInternal false, so both routes keep @internal
  declarations. The e2e equality journey now covers a stripInternal package.
- An enum member whose declared value is computed is refused with UnsupportedDeclaration
  naming the member, instead of losing its value.
- README documents the builder's refusals and the unrefused export * from another package.
- The plan is superseded by a new dated plan that adds U10 and KTD12, the Turbo evidence.
  KTD12 records that Turbo already owns build, cache and affected-task decisions here.

#4 (implicit exports) stays rejected: tsc 7.0.2 accepts importing a non-exported
interface from an import-bearing .d.ts (exit 0), and adding `export {}` makes the same
import TS2459.
…s emit options (ce-compound)

The fallback runs tsc -p <package tsconfig>, so a package's stripInternal (or any other
option that changes declaration text) diverges from the fast path's fixed options unless
the adapter pins it on the command line.
…s the adapter establishes it

The delta review noted that tsc -p still loads every other tsconfig option, so the doc no
longer claims the fallback text depends on the package only through source.
…#1, #2)

- The compiler fallback passes `--declarationDir` pointing at its scoped
  temp directory. A tsconfig `declarationDir` overrides `--outDir` for
  declarations, so tsc wrote into the analysed package and the adapter
  then refused it. The front-ends equality journey now covers a package
  that sets `declarationDir` and asserts nothing is written into it.
- The fast path reads an entry source only when it resolves inside the
  package root, sharing `relativeWithin` with the fallback's emitted-file
  filter. An entry outside the root was modelled on the fast path while
  tsc refused it (TS6059), so the two routes disagreed. It is now declined
  with NotIsolated and reaches the same refusal on the fallback.
- The compiler-fallback solution doc adds the output-location case.
oxc-transform prints `1_000` as `1e3` where tsc prints `1000`, so an
enum member or `const` initialised with `1_000` gave the fast path and
the compiler fallback different models (R3, R7). Numeric literals,
bigint literals and numeric property keys now print their value instead
of their source spelling. The builder feature gains a scenario pairing
oxc output with tsc's spelling of the same numbers.
- A declaration's release tag comes only from a doc comment after the
  previous top-level statement. The resolver took the last doc comment
  anywhere before the statement, so `/** @internal */` on one export,
  or an interface member's doc comment, tagged the exports after it.
- `ae-internal-missing-underscore` names only export names without a
  leading underscore, as api-extractor 7.59.1 does; `_hidden` was
  flagged too.
- The compiler fallback passes `--removeComments false`. A tsconfig
  `removeComments` stripped the `/** @public */` comment on that route
  only, so the routes disagreed on `ae-missing-release-tag`.

The upstream-oracle feature gains a scenario with a tagged export, an
interface member's doc comment and untagged exports after them; the
equality journey gains a `removeComments` package, and the solution
doc records the second text-changing option.
ryanleecode pushed a commit that referenced this pull request Oct 13, 2026
* test(api-extractor): retire the generated schema laws

The effect-schema-vite plugin wrote codec and recursion laws for every
exported schema into src/schema-laws.test.ts at test time. One of them,
∀x_TypeExprNesting_≤MaxDepth1, failed in CI run 38372869767 (PR #7's
merge ref): fast-check drew a TypeExpr nested deeper than the
recursionBudget it generates under. That law checks the test arbitrary,
not the codec: recursionBudget only shapes generation, and decoding has
no depth limit. Through the public surface, the shrunk value and one
nested three levels deeper decode, serialise byte-stably and keep every
node, and Verdict.judge still reports a change at the deepest member.

So the laws go, not the codec: vitest.config.ts drops inlineSchemaTests()
and the generated test file is deleted. No replacement test is added. The
property suite needs none of the plugin's budget materialization; all
103 remaining tests pass. main carries no in-source decode or verdict
refusal properties.

The plugin's devDependencies stay in packages/api-extractor/package.json:
removing them changes the 0.0.0 tarball the v0.0.0 tag records, so they
go in the first-release change. Hence the `none` intent.

* test(api-extractor): keep the recursion budgets governing generation

The schema-law plugin also ran effect-schema-recursion-budget's
transform, which turns each `recursionBudget` annotation into the
generator the property tests draw from. Without it, the budgets on
TypeExpr, Member and ApiItem shaped nothing, and only the retired
recursion laws would have reported that. The package's README says a
package that generates no schema laws registers recursionBudgetTransform
in its own Vitest configuration, so vitest.config.ts now does.

The transform's runtime import is resolved by plain lookup from the
schema modules, so the package is a devDependency of the private
workspace root, not of packages/api-extractor: editing that manifest
would change the 0.0.0 tarball. The lockfile gains the root importer
entry only; the tarball is the one already in .sfs-deps.
Under schema-laws/refusal-properties-cover-unobservable-boundaries
(systemfsoftware #761), each in-source refusal property from ruling #2
was judged against the public entry:

- The digit-spelling refusal is observable. TypeScript 7.0.2 prints an
  unresolved import specifier raw, so a package's own text can put
  `error TS007: ...` on tsc's stdout, and FrontEnd.analyse reports
  whether that line became a diagnostic. The property is removed; the
  e2e refusal journey now asserts that only tsc's own TS2882
  diagnostics come back. That journey fails at fd4ad93, which returned
  a forged code 7, and passes here.
- The Position cap refusal is not observable (Digits admits at most nine
  digits before the cap, and nothing outside the schema encodes), and its
  predicate restated the refinement rather than an independent oracle.
  The property is removed; the cap stays.
- The four Refuted seeds belonged to the generated laws #9 deleted; the
  seed store is removed.

The plan moves to a superseding dated file with U11.
Supersedes the 1629 plan. U12 gives every finding of the fd4ad93,
a2fe1eb, 2b2e595 and 2c13063 reviews, the delta passes, the anchor-50
notes and the pack rows a ruling: a named fix with a test, a rejection
with its reason, or a follow-up with an owner and a trigger. KTD3 is
amended: tsc stdout is package-influenced text, so the fallback reads
diagnostics from the TypeScript 7 API on one derived config and lists
emitted files from its scoped directory.
A diagnostic no compiler reported (a missing entry source, a missing
entry declaration, a tsc exit with nothing on record) printed as TS0, a
code TypeScript never defines. Diagnostic.code is now optional and, when
present, a positive integer, and refusal messages print TS<code> only for
a real code. Routing scenario: the missing-entry refusal message names no
code; it fails at 2c13063.
…er text exact (U12)

- C1 (P1): a local key now has a start and an end delimiter, so one key
  can never occur inside another and a text type keeps Options, Ids, Key
  instead of Opt_2ions, Id_2s, K_2ey.
- B5: a named optional tuple member prints its question mark.
- 2b2e595 setter note: the printer passed 'method' for every signature,
  so a set accessor in a printed type literal gained ': any'.
- C5/A16: one property-key resolver; each caller states its private-name
  decision. C9: one ordered keyword list. Both behaviour-preserving.
- A8, C8/A9: scenarios pin the interface and class item shapes and the
  declare global, declare module and external re-export refusals.
Each fix's scenario fails at 2c13063 and passes here.
…olver (U12)

- S1: a generic wrap<A> no longer hides the ae-forgotten-export of a real
  non-exported A in the same file; type parameters bind only within the
  declaration that introduces them.
- B9: an oracle scenario pins the full message array, order and
  de-duplication included.
- A18: the upstream comparison also compares the entry point.
  ExtractorMessage carries no item field, so item is not comparable.
  The fixture's temp dirs move to the system temp directory.
…(U12)

- A11: .cts source entries are read like .ts, .tsx and .mts.
- A12: a pattern export ("./*") expands to one entry per matching source
  under Node's exports rules: the * matches any substring, an explicit
  subpath wins, and a longer key prefix wins between patterns. Only the
  target's directory is walked, inside the package root.
Scenarios fail at 2c13063 and pass here.
- A10: an import whose specifier names no declaration file is reported,
  not dropped: the fast path declines naming the import, and the
  compiler route refuses it with UnsupportedDeclaration.
- D1: an entry declaration loads only from its recorded source, so a
  deleted mod.tsx is not modelled from a sibling mod.ts.
- D2: two entry points sharing a declaration path are declined.
- S3: the fast path compares real paths, so a symlink out of the package
  is not read.
- C7/A17: the entry mapping and the missing-entry list live once in the
  declaration graph.
Scenarios fail at 2c13063 and pass here.
…not tsc stdout (U12 C3)

tsc's stdout is package-influenced text: a module specifier holding a
newline prints a well-formed 'error TS7:' or 'file(1,1): error TS8:'
line, which parseCompilerOutput decoded as a real diagnostic. No line
grammar can tell them apart, so the adapter reads no stdout:

- One emit policy (compilerEmitPolicy), written with the package
  tsconfig as 'extends' into a derived config in the scoped directory;
  tsc runs as 'tsc -p <derived>'. The derived config carries the
  package's own typeRoots (from the API's parseConfigFile), else the
  default node_modules/@types chain, which TypeScript would otherwise
  resolve from the temp directory.
- Emitted declarations are the .d.ts files under the scoped directory.
- On a non-zero exit, diagnostics come from typescript/unstable/async on
  the same config, errors only, in tsc's reporting order, de-duplicated.
  A failed run the API has no error for (TS5033 on a long file name) is
  refused with one code-less diagnostic carrying tsc's text verbatim.
- Before tsc runs: a missing or escaping entry source (D1) and two
  entries sharing a declaration path (D2, which tsc 7.0.2 accepts and
  silently collapses) are refused.
- compiler-output.schema.ts is deleted, so the MessageText lookahead and
  the UNPARSED_FILED_LINE sentinel pack rows go with it.
- FrontEnd exports TYPESCRIPT_PRODUCER (C10), compilerEmitPolicy and
  derivedTsconfig. The profile harness writes the same derived config
  (B2/A6) and fails on a failed git (A15); the profile is not re-run.

The e2e refusal journey gains canonical sourceless and filed forged
lines, a package with no tsconfig, an entry excluded from include, a
251-character file name, an escaping entry and a shared declaration
path through CompilerEmitter (C6, A13, B10, D2). The equality journey
gains a package with declared typeRoots. At 3e81954 the refusal journey
returned codes 1234 and 4321 for the forged lines.
…e derived config (U12 C3)

A derived config in a temp directory resolves two defaults from its own
location: include (**/*) and typeRoots (@types from node_modules
upward). A package tsconfig that names no include therefore compiled
nothing (TS18003), and the default typeRoots chain would replace a
package's declared typeRoots. The adapter reads the package config
once with the TypeScript 7 API's parseConfigFile and carries its
resolved fileNames as files and its declared typeRoots, else the
default chain from the package root.

The equality journey gains a package with no include and one with
typeRoots: ["./types"]; each fails at 7520eae and passes here. The
solution doc and KTD3 record both settings, with the gate for each.
…er stamp (U12 C11)

README and the changeset now name the producer stamp as the one route
difference, and the README describes entry expansion (.cts, subpath
patterns), the entry checks that send a package to the fallback, and
that compiler diagnostics come from the TypeScript 7 API.
…ulings (U12)

Supersedes the 1737 plan. D1 named a reader refusal (MissingSource)
that would have reversed ruling #5's fast-path decline; the adapters
decline (fast path) and refuse before tsc (compiler). D2 said tsc
refuses a shared declaration path with TS5056; tsc 7.0.2 exits 0 and
writes one file, so the compiler adapter refuses it first. The
DeepSeek delta review's two findings are ruled: the D1 text finding
is accepted by this correction, the dropped-exit-cause finding is
rejected with its reason.
…inned type-unchecked (U20)

Review at 681efe3:
- maintainability P2: the model feature was 1120 lines again; its eight
  namespace scenarios move verbatim into 'Building the namespaces of a
  package API model' (896 and 233 lines).
- testing P2: no scenario showed the fast path skips the type checker.
  An annotated but type-incorrect package (count: number = 'one') takes
  FastPath and models count as a number. Pins existing behaviour.
…024 directories (U20)

A root pattern (./* -> ./*.ts) walked into node_modules, whose pnpm
symlinks fan out like any alias tree, so 24e9b67's bound refused the
package although no export name can pass through a node_modules segment.
The walk no longer enters a directory whose name is an invalid export
segment wherever that segment lies wholly inside the matched name; with
that, the bound drops to 1024. Reader scenario: a node_modules fan-out
of depth 13 is refused at 3016e54 and read now.

Pins (existing behaviour): a symlink to an ancestor two levels up is
walked once (DeepSeek delta gap); a manifest without name or version is
ManifestRejected (api-contract gap).
Supersedes the 0008 plan. Records the fixing commits for the walk bound
and node_modules pruning, cross-file scope, private constructors, index
signature names, namespace-body type and value spaces in the builder and
the resolver, the single classifier and the test split; the rejections
with their reasons; and the PR4 and PR5 follow-ups with their triggers.
…ed (U21)

Correctness residual at 5d32c3f: itemsForExport returned no items when
an export list named nothing, so a hand-written entry .d.ts holding
'export { present, Missing }' gave a model silently missing Missing, on
both routes. It is refused with UnsupportedDeclaration naming the file
and the export. Refusals scenario; fails at 5d32c3f with a model holding
only present. The README lists the cause.
…tays on the prefix (U21)

Review at 5d32c3f:
- DeepSeek peer P2: an export path found under a wide key (./* ->
  ./src/*.ts) kept its entry when a narrower key (./a/* -> ./vendor/*.ts)
  wins it in Node and names no file, so ./a/x was modelled although no
  consumer can import it. Each pattern entry is kept only when its key
  is the one Node's pattern-key order picks for the export path, which
  also leaves ./internal/z out under a null ./internal/*.
- DeepSeek peer P3: a key with two stars produced export paths holding a
  literal star; only single-star keys are patterns, and others name no
  entry.
- DeepSeek delta P3: a star inside a file name (./src*.ts) walked the
  package root, node_modules included, and tripped the walk bound. The
  walk enters only directories the target prefix can lead to.
Reader scenarios; each fails at 5d32c3f. Pin (existing behaviour): a
node_modules fan-out under src/ is pruned. The README states the rules.
…ck (U21)

Adversarial P2 and DeepSeek delta P2 at 5d32c3f: the resolver scoped a
namespace body with that block's names only, so with N declared twice
and an imported A, a reference to A in the second block was reported as
a forgotten export although tsc and the builder bind it to N.A. One pass
over each file maps a namespace path (N, N.M) to all its blocks, and
entering a block scopes it with their union. Analysis scenarios for a
top-level and a nested merge agree with upstream; both fail at 5d32c3f.
…ces; the changeset names new runtime dependencies (U21)

Testing and adversarial gaps at 5d32c3f: no scenario named a class or an
enum from both a type position and a typeof query, nor across merged
blocks. One pins both (existing behaviour).

api-contract P3: the release note now says the package installs
typescript 7, oxc-parser, oxc-transform and @microsoft/tsdoc at runtime.
Supersedes the 0042 plan. Records the fixing commits for merged
namespace scopes in the resolver, the unresolved-export refusal, Node's
winning pattern key, single-star keys and the prefix-bound walk; the
correction of U20 K1's fail-before depth; the pins; the rejections with
their reasons; and the follow-ups with owners and triggers.
Maintainability P3 at 9854ba8: the pre-pass and the reference scan each
kept a dotted namespace path. The pre-pass now also records each named
namespace's path by its start offset, and the scan looks it up instead of
keeping its own stack. No behaviour change: analysis scenarios unchanged.
…lists it (U22)

DeepSeek delta P3 at 9854ba8: an entry holding only `export * from './a.js'`
over `export { present, Missing }` was refused naming the entry, a file
that holds no such statement. The refusal now names the file whose export
map lists the name, found through star exports in resolution order.
Refusals scenario; fails at 9854ba8.

Maintainability P3: the builder's body-statement flatten was a copy of
the graph's statementsIn, which it now imports.
…ing pattern target (U22)

Testing gaps at 9854ba8: static fields, auto-accessors and accessor pairs
model exactly while a private field and a static block hold nothing;
template literal, predicate, asserts, indexed access, abstract construct
and mixed-signature object types keep canonical text; a pattern export
whose target directory does not exist adds no entry. Pins existing
behaviour.
…ckage (U22)

Adversarial P2 at 9854ba8: without the platform package typescript
installs as an optional dependency, every package needing the fallback
is refused with CompilerRejected carrying tsc's message, which names the
missing package (probed: the tsc shim exits 1 with 'Unable to resolve
@typescript/typescript-linux-x64').
…(U22)

Found pinning the testing gap at 9854ba8: `protected p` modelled as a
public `p`, an abstract method as a concrete one and a protected
constructor as `constructor`, so public->protected and concrete->abstract
changes were invisible to the diff. Following U20 K3 (`private
constructor`) and the existing `static` prefix, a member is named with
its modifiers in TypeScript's written order: protected, static, abstract.
Builder scenario; fails at eec0927.
Supersedes the 0108 plan. Records the fixing commits for member
modifiers, the star-export refusal origin, the native compiler
requirement and the two de-duplications; the pins; the rejections with
their reasons; and the follow-ups with owners and triggers.
…vate static member (U23)

Testing P2 and correctness gap at ea58038: two of the three abstract
element arms had no scenario. The class modifier scenario now holds an
abstract property, an abstract auto-accessor and a private static member;
it fails when either abstract arm is removed.
api-contract P3 at ea58038: the isolatedDeclarations refactor (U1) moved
the keyword list into a constant and dropped the comment from the public
KeywordName schema.
…used naming its file (U23)

DeepSeek delta P3 at ea58038: M2 named the listing file for an export
list that names no declaration, but an `export { x } from 'lodash'`
reached through the entry's `export *` was still refused naming the
entry, which holds no such statement. Both refusals now name the file
whose export map lists the name. Refusals scenario; fails at ea58038.
…o members (U23)

DeepSeek delta P3 at ea58038 read the split as a defect. TypeScript
admits only this split (TS2808 refuses a getter less accessible than its
setter, TS2676 mismatched abstractness), and two members say it exactly:
`w` reads publicly and only a subclass writes `protected w`. Pins
existing behaviour.
Supersedes the 0130 plan. Records the fixing commits for the external
re-export refusal origin, the abstract-arm pins and KeywordName's doc
comment; the accessor-pair pin; the rejections with their probes; and
the follow-ups with owners and triggers.
… resolved it (U24)

DeepSeek delta P3 at fa6bca2: N1 named the first star target listing
the name, so with `a.d.ts` listing `x` unresolved before `b.d.ts`
re-exporting it from `lodash`, the refusal paired `a.d.ts` with a
re-export it does not hold. An external symbol now carries the file
whose binding resolved it, and the refusal names that file; the
unresolved-name refusal keeps naming the first listing file.
Refusals scenario; fails at fa6bca2.
Testing gap at fa6bca2: U1 moved VerdictTypeId from an instance field to
Verdict.prototype for isolatedDeclarations, and only an orchestrator probe
read it. A verdict decoded from the cell's document holds the brand; the
scenario fails when the prototype brand is removed.
Correctness P2 (confidence 50) at fa6bca2 read a conditions object under
the family's source condition as a silently dropped entry. KTD5 reads the
condition where the family writes it, and its value is the source's
path; the README now says a condition holding anything else names no
entry, beside the nested and array forms, and the reader scenario pins
it. Pins existing behaviour.
Supersedes the 0143 plan. Records the fixing commits for the external
re-export's resolving file, the verdict brand pin and the source
condition's value rule; the rejections with their probes; and the
follow-ups with owners and triggers.
…the printer's helper (U25)

Maintainability P2 at c25bf9a: typeParameterNamesOf repeated the exported
typeParameterNames. No behaviour change; the analysis scenarios pass
unchanged.
Testing P3 at c25bf9a: the compiler's 'does not exist' refusal had no
scenario beside its pinned 'resolves outside the package' sibling. The
refusal journey analyses a package whose entry names no file: the fast
path declines and the compiler refuses with a Note naming src/mod.ts.
Pins existing behaviour (ruling #5).
Reliability P2 at c25bf9a: oxc-parser and oxc-transform load their
per-platform bindings at import, so without them (probed: both throw
'Cannot find native binding') importing the package fails. The README
states it beside the TypeScript 7 platform-package paragraph.
…del's KeywordName (U25)

Maintainability residual at c25bf9a: the printer and the model listed the
twelve keyword names independently, so a keyword added to the model would
refuse packages at runtime. The table is a record keyed by name and the
entries are derived from KeywordName.literals, so a missing key is a type
error (probed: dropping void fails tsc with TS2339).
…ts .tsx (U25)

Reliability gap at c25bf9a: the U14 ruling rested on an orchestrator probe
at a98496e. Pins existing behaviour.
Correctness residual at c25bf9a: the README named only the two-star key.
The reader scenario now pins both. Pins existing behaviour.
Supersedes the 0157 plan. Records the fixing commits for the resolver's
duplicate helper, the missing-entry refusal row, the oxc binding
requirement, the keyword table keyed by KeywordName, the directory
candidate pin and the two-star target; the rejections with their reasons;
and the follow-ups with owners and triggers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant