Repository navigation
v0.2.0
First public release. This section covers what changed since the 2026-10-06
prep; the untagged ## [0.1.0] section below (schema v1, the core engine, the
pac → pasco rename, brand flavor, the evidence registry) is the project's
pre-release history and was never tagged or published.
Added
-
CI — offline, self-tested link gate for the Markdown corpus (#149) —
scripts/link-check/(checker + fixture self-test first, thescripts/dep-gate
shape): every relative link/path in the tracked, non-hidden Markdown doc areas
must resolve to a file or directory, every in-page anchor must match its
destination's headings under GitHub's slug rules (the expected anchors are
committed asgithub-slugger@2.0.0output and asserted by the self-test), and
private targets go through an allowlist that requires a reason + removal
trigger. Offline and deterministic — external URLs are never fetched — so the
required check cannot depend on third-party uptime. Runs after the pinned Node
setup and beforepnpm install(fail fast). -
CI — every package type-checks its tests, and the wiring is gated (#181) —
each package gains atsconfig.test.jsonthat extends its own tsconfig (single
source for compiler options) and addstests/**, the root*.config.tsand
scripts/**;pnpm -r typechecknow covers the suites, so a mistyped shared
contract (e.g.@profile-as-code/paste/conformance) fails the gate. A node-only
scripts/typecheck-gateruns first and refuses a package whosetypecheck
script omits the test program — without it,pnpm -r typechecksilently skips
the package and CI stays green. -
Platform registry — per-platform metadata now has one machine-readable home (#154):
platforms/registry.yamlcarries the metadata that used to live hand-written across ≥7
surfaces (family, deploy kind, hosts, subdomain hosts, handle hosts, status, since, issue,
sources).pnpm gen:platforms(scripts/gen-platforms/, the D3 drift-check shape) projects
every derived surface:packages/core/src/generated/platforms.gen.ts(the host maps + slug
list consumed bydrift.ts/types.ts, plus the derived test enumerations), the SPEC §6
family table + count arithmetic and the README status counts between explicit
<!-- gen:… -->…<!-- /gen -->markers. The reviewed contract is unchanged — the schema
keeps$defs.platform.enum(D3) — and CI asserts registry ↔ enum ↔ generated agree
(scripts/schema-check/check.py+node scripts/gen-platforms/gen.mjs --check), including
the reverse assert: every enum member has a declared renderer or an explicit
renderer: false("not implemented yet"), and each renderer'sdeploySpecequals the
registrydeploy(D9). -
CONTRIBUTING — escalation path and measured response latency (#148) — the
published latency is measured, not a promise: a table over the three strata
(n=103 merged PRs, [MI], 2026-10-08), the regenerablegh+python3
one-liner, and the honest caveat that no external PRs exist yet so the figure
describes maintenance cadence. Stalled = no maintainer response after 7 days;
the escalation channel and the possible outcomes are named, and silence is not
one of them. -
Package READMEs + complete publication metadata (#143) — 9 new READMEs
(D8, English:packages/markdown+ the 8 renderers) with one uniform skeleton,
since npm always packsREADME.mdand publishing would otherwise show an empty
package page; the 10 libraries + the CLI gainrepository(+directory),
bugs,homepageandkeywords, and the pure libraries gain
sideEffects: false. -
hotmartplatform — schema additive v1.9, D16 operator extra #15 (49 total;
issue #137) — Hotmart: digital-products storefront + memberships (the Payment Policy's
recurring billing) + affiliate program, serving the Spanish/Portuguese-speaking markets
(offices in Belo Horizonte, Madrid, Bogotá, Ciudad de México — [OFICIAL] footer, fetched
2026-10-05), in thecreator-monetizationfamily. Enum
entries in$defs.platformand the derivedcheck.platform, ledger surface, drift host
hotmart.com(+HANDLE_HOSTS; the Hotmart Space profile URL shape stays[?]— no
PLATFORM_SUBDOMAIN_HOSTS entry without a verified shape), Zod regenerated (D3). Paste
platform (D9 — no management API documented for the profile body; developers.hotmart.com
covers sales/products [?]):pasco build hotmart→ paste →pasco status --mark.
Fees/eligibility sourced from the official legal texts, fetched 2026-10-05: no
monthly/annual fees ([OFICIAL] pricing); fixed fee BRL 2.49 / USD 1.00 / EUR 1.00 +
service fee 9.9% (20% micro-transactions) ([OFICIAL] Payment Policy §5); withdrawal
minimum BRL 2.00 + BRL 1.99 fee, 30/15-day deadlines ([OFICIAL] root FAQ #8); worldwide
eligibility, sanctions-based exclusions only, bank account in own name in the country of
residence after the first sale ([OFICIAL] ToU §A/§H/§2.8). EC payout rail remains
[?]— carried per the #133 precedent (kofi/payhipPayPal-EC), verification item
in the venture instance. The market-leadership framing in issue #137 was NOT carried into
this contract text (no independent source; Hotmart's own "world's largest / Latin
America's leader" wording lives in the skill research as attributed vendor marketing). -
Renderer
@profile-as-code/renderers-hotmart(v1.9, #137) — deterministic
plain-text profile paste-source: exactly ONE bios row (long > medium > short), byte-stable,
empty slice → empty artifact (skipped/unbuilt bypasco build, §15.1). Deploy spec is
§7'smanual(D9 paste platform). -
Skill
skills/hotmart/(#137) — SKILL.md (storefront frame — "esto resuelve X", not
tip-jar prose; no-results-promised rule per ToU §1.4) + playbook (paste flow with the
first-paste surface-verification step) + checks.yaml (soft-cap curacional — no documented
bio limit) + research/SOTA-2026.md (all sources fetched 2026-10-05; the fee table and
eligibility from the official legal texts; the Space URL shape and EC payout carried
[?]with sourcing leads). -
M9 floor — private-first, consent-gated AI-crawler posture (SPEC §12/§14, issue #141) —
the training-time corpus floor inverts:robots.txton published surfaces blocks
AI-training crawlers (CCBot / vendor AI crawlers) and user data ships without an open
license; entering corpora or open-licensing data requires the profile owner's explicit opt-in
(consent field — schema work deferred with Q10). Rationale: the engine is multi-tenant
(SPEC §9), so an allow-posture decides privacy + licensing on behalf of profile owners
(identity data entering training corpora without consent — GDPR-adjacent exposure when
productized); the operator's own profiles may opt in — that consent is theirs to give. Inputs -
Platform families
freelance+job-boards— SPEC docs-first declaration
(issue #138) — the platform families table in the SPEC (D16 block):freelance
covers the pre-existing v1upworkretroactively and names its backlog slice
(freelancer,workana,fiverr— filed with the gig-storefront boundary note);
job-boardsis a backlog-only slice (infojobs,computrabajo,indeed—
recruiter-facing CV surfaces; GEO hook = name-reputation in search results). No
enum/schema/core/CLI changes (48 unchanged); each backlog member follows the #133
member chain per slug when real operator demand lands, with an official eligibility
page before any claim. Boundary notes:linkedinstays un-bucketed (hybrid),
github-readme/github-org/researchgatestay un-declared (YAGNI),toptalis
recorded as a reject candidate (invite-only network,[MI]). -
Creator-monetization platform family — schema additive v1.8, D16 operator extras
#11–#14 (48 total; issue #133) —buymeacoffee,liberapay,gumroad,payhip: the
platforms where creators receive money (memberships / tips / recurrent donations /
storefronts) alongsidepatreon(v1) andkofi(v1.7). The family is declared docs-first
ascreator-monetization("Creator monetization platforms" — canonical industry term)
in SPEC (D16/§9) and the schema descriptions — no hard schema field (zero consumer
breakage). Enum entries in$defs.platformand the derivedcheck.platform, ledger
surfaces, drift hosts (buymeacoffee.com,liberapay.com,gumroad.com,payhip.com,
with Gumroad/Liberapay subdomain profile URLs), Zod regenerated (D3). Paste platforms
(D9 — no management API for the profile body):pasco build <platform>→ paste →
pasco status --mark. Per-platformgofundmedeferred (no EC payout as organizer,
operator decision in #133); per-platform skills remain separate follow-ups (D16 parity
caveat) and each carries its own sourced research when authored. -
Renderers
@profile-as-code/renderers-*for the family (v1.8, #133) —
renderers-buymeacoffee,renderers-liberapay,renderers-gumroad,renderers-payhip:
deterministic plain-text paste-sources (exactly ONE bios row, long > medium > short),
byte-stable, empty slice → empty artifact (skipped/unbuilt bypasco build, §15.1).
Core fixtureminimal-creator-monetization+schema-checkfixture
valid-creator-monetization.json. -
kofiplatform — schema additive v1.7, D16 operator extra #10 (44 total; issue #130) —
Ko-fi creator page (same family aspatreon): enum entries in$defs.platformand the
derivedcheck.platform, ledger surface, drift hostko-fi.com(+HANDLE_HOSTS), Zod
regenerated (D3). Paste platform (D9 — no API deploy; Ko-fi ships payment webhooks only):
pasco build kofi→ paste →pasco status --mark. -
Renderer
@profile-as-code/renderers-kofi(v1.7, #130) — deterministic plain-text
About paste-source: exactly ONE bios row (long > medium > short), byte-stable, empty
slice → empty artifact. Deploy spec is §7'smanual(D9 paste platform). -
Skill
skills/kofi/(#130) — SKILL.md (About: plain text, links only via Ko-fi's
Social-links feature, ≤40-char status note) + playbook (paste checklist + rename-drift
caveat) + checks.yaml (About soft-cap — Ko-fi documents no bio limit) + research/
SOTA-2026.md (all sources fetched 2026-10-05). -
schema v1.6:
identity.links[].role— entity identity gets a single home (SPEC §15.4, issue #103) —
the engine's JSON-LD had no owner:geo.tsand both renderers each assembled the entity
block in parallel, which is how SPEC §15.4's@idrequirement was never implemented — it fell
through the seam between three copies. (The repo already solved this forhasCredential, §18.9
credentialLdEntries, "Single source for every emitter"; this applies it to the entity.)
New fieldlinks[].role—identity|realization(default) |aggregator, a translation
layer ontoschema.org:url/sameAs/mainEntityOfPage(never presented as standard
vocabulary). Absent role =realization, so no workspace needs editing for the field itself.
New modulecore/src/structured-data.tsowns@id,sameAsand the entity fields;
geo.tsand both renderers consume it. New checkgeo/entity-id;geo/sameas-linksnow
counts onlyrole: realizationlinks (it previously credited a citability signal the JSON-LD
never emits). Weights rebalanced:entity-id0.05 +structured-person0.05 = the 0.10 budget
the field-check alone held (weights sum to 1 by contract; a test enforces it).⚠️ Rendered output changes for existing workspaces. Workspaces keep validating unchanged,
but the rendered output does change — three deltas: (a)github-readmegains
jobTitle/worksFor, which it never emitted althoughgeo.tscomputed them; (b)sameAs
narrows torole: realizationand now includes claimedsocialHandles[].url— for a
github-orgworkspace that is byte-identical (it already did both), but a person workspace
with claimed handles gains them (the oldgithub-readmedid not); (c)geoCheckadds
geo/entity-id, so a workspace declaring norole: identityloses up to 5 points.
Re-render and review the diff.Design decisions (operator, 2026-09-24): an absent
@idnever suppresses the JSON-LD
block — suppressing it would strip structured data from every workspace declaring norole,
i.e. the feature would remove signal that already worked;geoCheckreports the gap instead
(geo/entity-id), naming the actual situation (a declared-but-ineligible aggregator is reported
as such, never as "nothing declared"). Severalrole: identitylinks → the first wins
(declaration order): an earlier draft ranked "own domain > social network" via hostname patterns
and was deleted — it meant a second, hand-maintained platform list besidePLATFORMS(D16)
and a product judgement inside a regex. Classification + sources:identeco-cifereca.
Tests: +11 (packages/renderers/github-readme/tests/role.test.ts,packages/core/tests/geo.test.ts). -
release:
release.ymlcrea el GitHub Release desdeCHANGELOG.md(issue #58) — el
publish a npm no anunciaba la release: el tag quedaba sin página ni notes, y el item 3 del
gate (docs/STANDARDS.md) no se podía firmar. Jobreleaseseparado y después de
publish(una página para una versión que no llegó a npm sería peor que una tardía):
checkout del mismo commit verificado, la sección de la versión se extrae con
packages/cli/scripts/release-notes.mjs(match exacto —0.1no satisface0.1.0— y el
cuerpo termina en el próximo heading##, así[Unreleased]no se filtra) y
gh release create … --verify-tag. Idempotente (un retryworkflow_dispatchno toca una
página existente);contents: writeacotado a ese job. La sección se valida antes del
publish (unnpm publishno se deshace: descubrir las notes faltantes después dejaba la
versión consumida con el tag vacío, el síntoma que #58 elimina). El extractor endurecido en la
review fresh: ignora##dentro de fences de código (antes truncaba las notes en silencio),
no absorbe definiciones de link al final del archivo, acepta--key=value, y el main-guard
compara realpaths y falla ruidoso en vez de no-opear (clase #81). La preparación del release
([Unreleased]→## [<version>]) queda documentada endocs/ci-cd.md. Tests: 15
(packages/cli/tests/release-notes.test.ts); runbook endocs/ci-cd.md. -
style gate: Biome (lint + format + import order) in CI and
pnpm lint(issue #59) — one tool,
one config (biome.json), one command; CI runs it before tests (fail fast). Gate semantics:
pnpm lintfails on Biome errors;recommendedwarn-level rules are reported without failing
(Biome's default). One deliberate strictness bump:noNonNullAssertionis an error outside
tests and off under**/tests/**(wherearr[0]!is idiomatic) — both sides verified by
flipping the rule red/green. The four intentional control-character regexes in the markdown
escaping contract carrybiome-ignorecomments with reasons. Scope: Biome does not do type-aware
linting —pnpm typecheckstays the type gate. Fix scripts are risk-split:pnpm lint:fixapplies
safe fixes only; Biome's unsafe fixes (semantics-adjacent, e.g.useTemplate,
useLiteralKeys) are opt-in viapnpm lint:fix:unsafeand the resulting diff is reviewable.
One-time formatter baseline applied
(whitespace-only forschema/+ fixtures; byte-stable behavior verified: 268 tests green, D3
gen:zoddiff exit 0, schema contract self-test OK). Rationale and revisit triggers:
docs/ci-cd.md§Why Biome. -
Published-surface validators — publint +
@arethetypeswrong/cli(issue #142):
packages/cligainsprepublishOnly: publint— npm runs it inside
npm publish(dry runs included), so the irreversible step self-validates its
manifest (attwstays out of that hook on purpose: its--packmode inherits
npm'snpm_config_dry_run=trueundernpm publish --dry-runand dies on the
missing tarball, and the bin-only CLI has no types for it to check);
ci.ymlruns both validators on every PR (publintover every
workspace package,attw --pack --profile esm-onlyover the packages that ship
atypesentry — the set is derived from the manifests, not a hand-maintained
list). The typed packages are ESM-only by design, hence the profile.
docs/ci-cd.mddocuments the publish chain (guard → build →verify:pack→
prepublishOnly→ publish) and the post-publish verification runbook
(npm view+ publint/attw against the published tarball).
Changed
-
Repo config & supply-chain hardening (2026-10-09 audit) — actions
(checkout,setup-node,pnpm/action-setup) pinned to full commit SHAs;
persist-credentials: falseon every checkout whose later steps need no git
auth (dco.ymland the release guard keep it on purpose — they rungit fetch); pnpmminimumReleaseAge: 4320,blockExoticSubdeps: trueand
trustPolicy: no-downgrade; pinned tool installs (jsonschema==4.26.0,
npm@11.6.1);runs-on: ubuntu-24.04withtimeout-minutes+concurrency;
.gitattributesLF normalization and.github/CODEOWNERS.docs/ci-cd.md
records what still waits on a public repo / GitHub Pro: the Actions SHA-pinning
policy, a tag-protection ruleset forv*, secret scanning, and an environment
approval gate on publish. -
chokidarresolved to the attested 4.0.1,trustPolicyExcludedropped
(#184) — the transitive lockfile entry (tsup's file watcher) tripped
no-downgrade; instead of excluding the untrusted version the graph resolves
an attested one. 4.0.1 is same-major and behaviorally identical — the
4.0.1→4.0.3 delta is TypeScript types only ([OFICIAL] upstream releases) — and
5.0.0 (also attested) is a breaking, ESM-only major tsup's lazyrequiredoes
not target. -
One-home codegen and build wiring (#152, #164, #165) — three
hand-maintained mirrors removed: thePLATFORMSmirror is gone (#152; the list
is now theplatforms/registry.yamlprojection — see the #154 entry);
packages/clibuilds from the workspace dependency graph
(pnpm --filter "@profile-as-code/cli^..." build) instead of an eight-entry
hand-written renderer chain (#164); the platform↔renderer mapping moves into
each renderer's ownpascomanifest block, projected into a generated CLI map
that a parity test keeps in sync (#165). -
dep-gate fixtures are undiscoverable data (#166) — the fixture manifests
are committed asfixture.package.json/fixture.pnpm-workspace.yamland
materialized under their real names in a temp dir byscripts/dep-gate/test.mjs
before the productioncheck.mjsruns over them, so Dependabot can no longer
discover (and fail on) an intentionally invalid workspace manifest. The
production checker is unchanged and stays the tested path;exclude-paths
remains documented as belt-and-braces for the version-update path. -
Paste-renderer core — the invariant gets one home (#150, PR #158): the six paste
adapters (kofi…hotmart) carried a byte-identical render body (sha256[:16]
b00031017ee2bbc4after comment strip). The shared body — length priority
long > medium > short, EXACTLY ONEbiosrow's verbatim text +\n,""on an empty
slice — now lives in a dedicated shared module (pasteSource(); re-homed to
packages/paste/in #157 below): the
structured-data.tsv1.6 /packages/markdownprecedent — a contract shared by every
adapter lives in one module, and adapters delegate rather than re-implement.kofi
migrates first: its suite passes, with the overclaiming "emits plain text" fixture check
tightened to exact-equality verbatim pass-through — identical input → identical output is
the byte-preservation proof.pasco buildoutput is unchanged (byte-stable; empty slice →
unbuild, §15.1). The remaining five adapters and the six copied suites landed in #157. -
Paste-renderer core — the seam gets its own support package; all six paste adapters
delegate (#157): the shared body moved fromcore/src/paste-source.tsto
packages/paste/(@profile-as-code/paste) — a renderer support package (the
packages/markdownprecedent), so the engine stays an engine and vitest never enters
core's runtime. The conformance body ships as the test-only
@profile-as-code/paste/conformancesubpath; every adapter suite now invokes it
(describePasteConformance) and keeps only its platform facts (deploySpec: "manual",
no assets). The six copied suites and the four overclaiming "emits plain text"
assertions are gone, and the adapter docblocks now state the honest pass-through.
pasco buildoutput is unchanged (byte-stable; empty slice → unbuild, §15.1). -
Family wording normalization —
creator-support→creator-monetization(#133) —
the informal prose from #130 (SPEC D16/§9 and thekofiv1.7 description line in
schema/profile.schema.json, this file included) now carries the canonical term;
already-merged history keeps the old wording, and this note records the rename
(operator decision, 2026-10-05, issue #133). -
pasco build— empty renderer output is now a complete unbuild lifecycle (v1.7, #130)
An empty render is not a deploy artifact, andprofiles/is a pure function of the
workspace (§15.1): the build skips BEFORE creating the directory (no litter on
workspaces without the row) and REMOVES a previously writtenprofiles/<platform>/
output (README.md+assets/) when its bios row disappears — a stale file would be
re-pasted otherwise, andpasco statusnever reads artifact files. The build report
records the removals asskipped: [{platform, reason}]. -
release auth: publish migrado a trusted publishing (OIDC), sin
--provenance(issue #80) —
el camino actual usaba un granular access token con bypass 2FA, que npm retira del publish directo
~enero 2027 ([WEB] github.blog changelog 2026-07-08).release.ymlahora prepara el runner para
OIDC (npm >= 11.5.1— con Node 22 llega npm 10.x, que ni intenta el intercambio OIDC y falla con
unE404/E403engañoso — e imprimenpm --versionpara descartar la causa de un vistazo) y
elimina el flag condicional--provenance: bajo OIDC npm genera la atestación sola y la omite
cuando el repo fuente es privado.NPM_TOKENqueda solo como fallback del primer publish
(el que corre antes de que el paquete exista y se pueda configurar el trusted publisher); npm
prefiere OIDC sobre el token, así que la migración no exige borrar nada en el mismo paso. Pasos
humanos y pre-flight (workflow filename,repository.url) endocs/ci-cd.md.
Fixed
-
permanent Dependabot
failureruns onmain(#160) — the security job
Dependabot opened over the dep-gate fixtures aborted on
dependency_file_not_parseablebecauseexclude-pathsapplies only to the
version-update path ([OFICIAL] dependabot-options-reference), and the permanent
failing run also poisonedissue-select's "main CI green" gate. The
placeholder-version alerts are ignored by exact version and thesource-map-js
override floor documented; the class is eliminated by construction in #166. -
hardening de semántica de vacío: geo alineada con empty=absent + strings planos vacíos interpolados + typing de
langMap (issue #86) — tres follow-ups de la review fresh de #85, una familia (semántica vacío/typing alrededor de
la regla del accesor de texto #84):- geo (F1, MAJOR) —
geoChecklee el perfil crudo y divergía internamente:structuredData(lectura falsy de
.en) tratabamission: {en: ""}como ausente, pero el checkgeo/structured-personcreditaba
description(mission): yesconBoolean({en: ""})sobre el objeto. El check ahora lee desd.fields
(structuredData como fuente única de presencia de campos de entidad) — parity byte-idéntica para todo valor
schema-válido poblado (logo/established/photo son url/date no-vacíos,location: ""es falsy en ambas lecturas);
solo el caso degenerado""deja de creditar readiness.langValuesfiltra""del haystack de refuerzo
(inerte hoy —"".includes(ancla)nunca matchea — invariante explícito para predicates futuros, sin trim).
Pins de regresión:location: ""yrole: {en: ""}cuentan como ausentes (person) ymission: {en: ""}deja de
creditardescription(mission)(brand, score 2/3 con logo+established). - strings planos vacíos (F2, NIT) — sweep de strings planos sin
minLengthque se interpolan:
newsletter.cadence: ""(schema-válido) producía fila pública con·colgante en github-org → "" = ausente en
el sitio de interpolación (check truthy, regla del string vacío exacto, sin trim). El sweep encontró un segundo
sitio vivo del mismo síntoma: items vacíos deskill.keywords[](sinminLength) joineados crudos producían
seams, ,en github-readme → filtrados (item vacío = ausente; lista toda-vacía → sin colon, como #84).
Sweep verificado seguro/inalcanzado:podcast.status(enum),press.outlet/experience.org/
publications.title/venue/artifact.license(minLength: 1),publication.doi/orcidId(pattern),
identity.handle/timezone(no renderizados raw hoy),links[].label(fallback hostnamePath de #78),
links[].platform(miss de BADGE), arrays inertes para render (project.keywords,
skill.geoKeywords,brandVoice,artifact.tags— pesos/selección/skill-facing),semanticAssociations
(trim+filter previo). Revisión (post-review de #87):project.keywordsNO era inerte para el
matching de pesos — los Sets deprojectWeightmatcheaban items""entre sí y creditaban el peso de
la skill a un proyecto sin keywords (orden/featured del README alterados; verificado). Los dos sitios de
keywords ahora comparten un helpernonEmpty(render + matching): item vacío = ausente en ambos, la regla
del accesor se expresa una vez por archivo. Bordes pinneados:{en: "", es: "…"}es ausente para
structured-data (en-canónico) y whitespace-only sigue siendo dato (sin trim). - typing de langMap (F3, NIT — issue-first por D3) —
$defs/langMapno tipaba sus valores:
{en: 0},{en: []},{en: {}}eran válidos en JSON Schema mientras todo consumidor asume
Record<string, string>(contraste:langMapListsí los tipa). Fix solo en el contrato:
additionalProperties: {type: string}+ nota en description.pnpm gen:zodregenerado — el generador ya
materializaba el shape langMap (catchall string), así que el Zod generado queda byte-idéntico: sin cambio de
engine, la frontera Zod ya rechazaba y ahora está pineada enload.test. Fixture
invalid-langmap-nonstring.jsonen el self-test de contrato (schema-check). Los strings vacíos siguen
schema-válidos por diseño (regla #84: vacío = ausente en el accesor).
Byte-parity: inputs poblados byte-idénticos (tests EXPECTED sin cambios). TDD: 4 rojos (brand/geo 1, github-org 1,
github-readme 1, schema-check fixture 1) + 2 pins verdes (frontera Zod, parity person) → verde; suite 265/265;
schema-check OK; typecheck limpio. - geo (F1, MAJOR) —
-
strings vacíos en langMaps = ausentes (issue #84, opción (a) ratificada) — los valores
en: ""eran
schema-válidos (sinminLength) y los accesores los trataban como texto presente (!== undefined): filas
degeneradas en outputs públicos (- **hub** —con dash colgante,Evidence: ([source])con doble espacio,
- ** **vacío en github-org). Regla semántica única en el accesor de texto:langText(core,compile.ts) y los
pickTextde ambos renderers devuelvenundefinedpara""— la vacuidad cuenta como ausente en AMBOS niveles del
lookup (unes: ""cae alen; unen: ""no emite nada). Sin ghost-fallbacks en el registro defallbacks
(solo se anota cuando efectivamente se devolvió texto). Sin trim (el límite de la regla es el string vacío exacto —
" "queda como dato, candidato para un check futuro). Byte-parity: workspaces poblados byte-idénticos (tests
byte-for-byte sin cambios); solo outputs previamente degenerados mejoran. TDD: 5 tests rojos (core 3, readme 1,
org 1) → verde; suite 258/258. -
github-readme: campos opcionales nunca interpolan"undefined"(issue #78) —identity.links[].labeles
opcional en el schema (solourles requerido, y el string vacío es válido — sin minLength) y el renderer la
interpolaba cruda: un workspace válido sinlabel(o conlabel: "") publicaba- [undefined](…)o- [](…)en
un README público. Fallback opción (b) del issue:hostnamePath(l.url)— exactamente el label que ya usan los
links deprojects[], sin cambio de contrato. El sweep del patrón (misma clase, mismo archivo) encontró dos
instancias más y las arregla en el mismo PR:skill.level(omisión del paréntesis) yproject.status(omisión del
segmento· \status`). Byte-parity: inputs con los tres campos poblados emiten byte-idéntico (test EXPECTED sin cambios);github-org` verificado limpio (todas sus interpolaciones opcionales están guardadas). -
exportscondition order in the five newest paste renderers
(buymeacoffee,liberapay,gumroad,payhip— v1.8;hotmart— v1.9,
issue #142):"types"now precedes"import"in the.entry, matching
every earlier package in the workspace. publint flags the old order as an
error — exports conditions are order-sensitive for TypeScript resolution, so
thetypescondition must come first. Caught before the first npm publish:
none of these packages is on the registry yet.