Thirteen of the nineteen JS services that still assigned a captured response to _webout now emit their members through the runtime's webout(). The envelope comes from the runtime, so SYSCC is numeric, SYSDATE/SYSTIME are real, SYSSITE is reported, and the SAS-only _METAUSER/_METAPERSON are gone.
One value is corrected: the capture wrote the OMS URI as OMSOBJ:Property\A59..., but \A is an escape in a JS template literal, so the backslash was consumed and the client received a URI without it. It now carries the backslash the SAS metadata server returns. Everything else is unchanged.
SYSCC on metarepos was "4" in the capture - an error code from the recorded day - and the client reads it as a SAS error. It is 0 now.
These payloads were taken from the response the old service serves, so the comparison is against the real previous output rather than a transcription.
One correction since the first version of this PR: the captures had already lost the OMS and path separators - OMSOBJ:SASLibrary\A59... written inside a template literal is the escape \A, so the old services served OMSOBJ:SASLibraryA59..., D:MyDemoSankey and BUILTINAdministrators. 7d9d466 restores seventeen of those values across five services (see the commit for the field list and the verification). Comparison against the served response cannot catch this class of defect, because the corruption is in the thing being compared against.
Verification
Both trees deployed on one server (base /Public/app/dc, branch /Public/app/dcnew) and every service POSTed to both. Parsed JSON compared with the envelope keys excluded. Every member is equal row for row except the nineteen separator-corrections described above (seventeen here, two in metadetails).
metanav.cy.ts passes against the converted services (1/1, 48s) on a seeded 3.13.0 mock estate.
Not converted
lineage/fetchlineage is left alone deliberately. Its capture is not valid JSON - bare quotes inside the graphviz labels, raw newlines inside the STRING values, and a \A escape that already ate a backslash - so the client cannot parse the response today. There is no working payload to preserve, and the intended text is a decision, not a rewrite. Nothing in client/src requests lineage/fetchlineage either, so it looks dead; either delete it or rebuild it, separately.
sas/mocks/sas9/ (target sas9-mocks, 35 files) also still assigns _webout directly and carries the same separator losses. Deliberately out of scope here so the review covers one tree; tracked as #463.
The five raw-body services stay: admin/exportdb, admin/makedata (HTML), public/getrawdata, public/getddl (own Content-Type and Content-Disposition), and auditors/getauditfile's browser fallback (a plain JSON document, not an envelope). webout() has no raw-body or header call.
Thirteen of the nineteen JS services that still assigned a captured response to `_webout` now emit their members through the runtime's `webout()`. The envelope comes from the runtime, so `SYSCC` is numeric, `SYSDATE`/`SYSTIME` are real, `SYSSITE` is reported, and the SAS-only `_METAUSER`/`_METAPERSON` are gone.
## metanav (e1d877f)
`outrepos` (2), `objects` (1), `types` (71), `attributes` (12), `associations` (2).
One value is corrected: the capture wrote the OMS URI as `OMSOBJ:Property\A59...`, but `\A` is an escape in a JS template literal, so the backslash was consumed and the client received a URI without it. It now carries the backslash the SAS metadata server returns. Everything else is unchanged.
`SYSCC` on `metarepos` was `"4"` in the capture - an error code from the recorded day - and the client reads it as a SAS error. It is `0` now.
## lineage, usernav, refreshlibinfo (1f54fad)
| service | members |
|---|---|
| lineage/fetchtablelineage | info, finalfinal, flatdata, idlookup |
| lineage/getmetacols | metacols (11) |
| lineage/getmetatables | metatables (1) |
| usernav/usermembers | users (2) |
| usernav/usermembersbygroup | sasmembers (2) |
| usernav/usergroupsbymember | groups (1) |
| usernav/usermembersbyrole | sasgroups (1), sasmembers (1) |
| usernav/userroles | roles (46) |
| public/refreshlibinfo | libinfo (1) |
These payloads were taken from the response the old service serves, so the comparison is against the real previous output rather than a transcription.
One correction since the first version of this PR: the captures had already lost the OMS and path separators - `OMSOBJ:SASLibrary\A59...` written inside a template literal is the escape `\A`, so the old services served `OMSOBJ:SASLibraryA59...`, `D:MyDemoSankey` and `BUILTINAdministrators`. `7d9d466` restores seventeen of those values across five services (see the commit for the field list and the verification). Comparison against the served response cannot catch this class of defect, because the corruption is in the thing being compared against.
## Verification
Both trees deployed on one server (base `/Public/app/dc`, branch `/Public/app/dcnew`) and every service POSTed to both. Parsed JSON compared with the envelope keys excluded. Every member is equal row for row except the nineteen separator-corrections described above (seventeen here, two in `metadetails`).
`metanav.cy.ts` passes against the converted services (1/1, 48s) on a seeded 3.13.0 mock estate.
## Not converted
`lineage/fetchlineage` is left alone deliberately. Its capture is **not valid JSON** - bare quotes inside the graphviz labels, raw newlines inside the `STRING` values, and a `\A` escape that already ate a backslash - so the client cannot parse the response today. There is no working payload to preserve, and the intended text is a decision, not a rewrite. Nothing in `client/src` requests `lineage/fetchlineage` either, so it looks dead; either delete it or rebuild it, separately.
`sas/mocks/sas9/` (target `sas9-mocks`, 35 files) also still assigns `_webout` directly and carries the same separator losses. Deliberately out of scope here so the review covers one tree; tracked as #463.
The five raw-body services stay: `admin/exportdb`, `admin/makedata` (HTML), `public/getrawdata`, `public/getddl` (own Content-Type and Content-Disposition), and `auditors/getauditfile`'s browser fallback (a plain JSON document, not an envelope). `webout()` has no raw-body or header call.
The four metanav services assigned a captured 2022 response to _webout, envelope
and all, instead of using the runtime's webout() like every other service. The
members are unchanged:
metarepos outrepos 2 rows
metaobjects objects 1 row
metatypes types 71 rows
metadetails attributes 12 rows
associations 2 rows
Both trees were deployed on one server, at /Public/app/dc and /Public/app/dcnew,
and the responses compared as parsed JSON with the envelope keys excluded:
outrepos, objects and types are equal row for row.
metadetails changes in one place. The capture wrote the OMS URI as
OMSOBJ:Property\A59..., but \A is an escape in a JavaScript template literal,
so the backslash was consumed and the client received OMSOBJ:PropertyA59... -
a URI that cannot resolve. The member now carries the backslash the SAS
metadata server returns.
The envelope comes from the runtime now, which also drops three artefacts of
the recording: SYSCC "4" on metarepos (an error code from the captured day,
which the client reads as a SAS error), SYSDATE/SYSTIME frozen at 26SEP22, and
the SAS-only _METAUSER/_METAPERSON.
The other nine services that answer with a captured response also assigned it to
_webout, envelope and all. They now emit their members through the runtime's
webout():
lineage/fetchtablelineage info, finalfinal, flatdata, idlookup
lineage/getmetacols metacols 11 rows
lineage/getmetatables metatables 1 row
usernav/usermembers users 2 rows
usernav/usermembersbygroup sasmembers 2 rows
usernav/usergroupsbymember groups 1 row
usernav/usermembersbyrole sasgroups 1 row, sasmembers 1 row
usernav/userroles roles 46 rows
public/refreshlibinfo libinfo 1 row
Each payload was taken from the response the old service serves, then both
trees were deployed on one server (/Public/app/dc and /Public/app/dcnew) and
compared as parsed JSON with the envelope keys excluded: all nine are equal row
for row, including the escaped values (the OMS URIs keep their backslash, and
refreshlibinfo keeps the quoted paths).
The envelope is the runtime's now, so SYSCC is numeric 0 rather than the string
"0", SYSDATE/SYSTIME are today's, SYSSITE is reported (these captures carried a
lower-cased syssite, which the client never read), and the SAS-only
_METAUSER/_METAPERSON are gone.
lineage/fetchlineage is deliberately left alone: its captured body is not valid
JSON today (bare quotes in the graphviz labels, raw newlines inside the STRING
values, and a lost backslash), so there is no payload to preserve and the
intended text needs a decision rather than a rewrite. Tracked separately.
hermes
changed title from feat(mocks): emit the metanav responses through webout() to feat(mocks): emit the captured-literal services through webout()2026-11-04 11:46:30 +00:00
The captures wrote values like OMSOBJ:SASLibrary\A59LNVZG.B500000K inside a
template literal, where \A is an escape, so the separator never reached the
client: it served OMSOBJ:SASLibraryA59LNVZG.B500000K. The same happened to
D:\MyDemo\Sankey and BUILTIN\Administrators. Converting the service to webout()
faithfully preserved the corrupted value - and the metadetails ASSOCURI, the one
field fixed by hand at conversion time, was left as the only member that kept its
separator, which is the inconsistency the review named.
Seventeen values corrected across five services:
lineage/fetchtablelineage info[0].LIBURI 1
lineage/getmetacols metacols[].COLURI 11
lineage/getmetatables PATH_SCHEMA, TABLEURI 2
public/refreshlibinfo libinfo[0].OWNERS 1
usernav/usermembersbyrole sasgroups[].URIMEM,
sasmembers[].URIMEM 2
Verified by deploying both trees on one server and comparing the parsed
responses: those seventeen fields are the only differences, and every other
member of all thirteen converted services is unchanged (metadetails' two
ASSOCURI, already carrying its separator, still differ from the capture for the
same reason).
Anything that splits a URI or a Windows path on the separator, or displays
OWNERS or PATH_SCHEMA, now sees the value the SAS metadata server returns.
Both findings are right, and the first one is worse than "the diff lost some escaping" - I propagated a corrupted value and then described it as preserved.
1. Separator loss - fixed in 7d9d466.
Root cause: the captures wrote OMSOBJ:SASLibrary\A59LNVZG.B500000K inside a template literal, where \A is the escape A. The separator was already gone in the value the old service served - OMSOBJ:SASLibraryA59LNVZG.B500000K - so my two-deployment diff compared against corrupted data and called it equal. metadetails kept its separator only because I fixed that one by hand when converting it, which is exactly the inconsistency you spotted.
Seventeen values corrected:
service
field
count
lineage/fetchtablelineage
info[0].LIBURI
1
lineage/getmetacols
metacols[].COLURI
11
lineage/getmetatables
PATH_SCHEMA, TABLEURI
2
public/refreshlibinfo
libinfo[0].OWNERS
1
usernav/usermembersbyrole
sasgroups[].URIMEM, sasmembers[].URIMEM
2
PATH_SCHEMA is D:\MyDemo\Sankey and OWNERS is BUILTIN\Administrators; the OMS URIs take the separator before the repository id.
Verified: both trees deployed on one server (/Public/app/dc base, /Public/app/dcnew branch), all thirteen converted services POSTed to both, parsed JSON compared. Those seventeen fields plus the two metadetails ASSOCURI are the only differences; every other member of every service is unchanged.
I have corrected the PR body, which claimed the escaped values kept their backslash - they did not, and the claim came from the same faulty comparison.
On assertions: there is no Cypress spec for the lineage, user, group, role or metadata screens, so nothing covers the separator either way - the deploy-diff is the guard. Worth its own change rather than smuggling a spec in here.
2. sas/mocks/sas9/ - a conscious call: out of scope for this PR. It is a separate target (sas9-mocks, appLoc /User Folders/sasdemo/), 35 files, and it needs its own deploy-and-diff against its own app location, so folding it in would double the blast radius of a review that is already about one tree. Tracked as #463 with the acceptance criteria and the note that it also carries the same separator losses.
Merge note: this branch now depends on nothing outside itself; #462 (pin 3.14.0) is separate and independent.
Both findings are right, and the first one is worse than "the diff lost some escaping" - I propagated a corrupted value and then described it as preserved.
**1. Separator loss - fixed in `7d9d466`.**
Root cause: the captures wrote `OMSOBJ:SASLibrary\A59LNVZG.B500000K` inside a *template literal*, where `\A` is the escape `A`. The separator was already gone in the value the old service served - `OMSOBJ:SASLibraryA59LNVZG.B500000K` - so my two-deployment diff compared against corrupted data and called it equal. `metadetails` kept its separator only because I fixed that one by hand when converting it, which is exactly the inconsistency you spotted.
Seventeen values corrected:
| service | field | count |
|---|---|---|
| lineage/fetchtablelineage | `info[0].LIBURI` | 1 |
| lineage/getmetacols | `metacols[].COLURI` | 11 |
| lineage/getmetatables | `PATH_SCHEMA`, `TABLEURI` | 2 |
| public/refreshlibinfo | `libinfo[0].OWNERS` | 1 |
| usernav/usermembersbyrole | `sasgroups[].URIMEM`, `sasmembers[].URIMEM` | 2 |
`PATH_SCHEMA` is `D:\MyDemo\Sankey` and `OWNERS` is `BUILTIN\Administrators`; the OMS URIs take the separator before the repository id.
Verified: both trees deployed on one server (`/Public/app/dc` base, `/Public/app/dcnew` branch), all thirteen converted services POSTed to both, parsed JSON compared. Those seventeen fields plus the two `metadetails` ASSOCURI are the only differences; every other member of every service is unchanged.
I have corrected the PR body, which claimed the escaped values kept their backslash - they did not, and the claim came from the same faulty comparison.
On assertions: there is no Cypress spec for the lineage, user, group, role or metadata screens, so nothing covers the separator either way - the deploy-diff is the guard. Worth its own change rather than smuggling a spec in here.
**2. `sas/mocks/sas9/` - a conscious call: out of scope for this PR.** It is a separate target (`sas9-mocks`, appLoc `/User Folders/sasdemo/`), 35 files, and it needs its own deploy-and-diff against its own app location, so folding it in would double the blast radius of a review that is already about one tree. Tracked as #463 with the acceptance criteria and the note that it also carries the same separator losses.
**Merge note:** this branch now depends on nothing outside itself; #462 (pin 3.14.0) is separate and independent.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Thirteen of the nineteen JS services that still assigned a captured response to
_weboutnow emit their members through the runtime'swebout(). The envelope comes from the runtime, soSYSCCis numeric,SYSDATE/SYSTIMEare real,SYSSITEis reported, and the SAS-only_METAUSER/_METAPERSONare gone.metanav (
e1d877f)outrepos(2),objects(1),types(71),attributes(12),associations(2).One value is corrected: the capture wrote the OMS URI as
OMSOBJ:Property\A59..., but\Ais an escape in a JS template literal, so the backslash was consumed and the client received a URI without it. It now carries the backslash the SAS metadata server returns. Everything else is unchanged.SYSCConmetareposwas"4"in the capture - an error code from the recorded day - and the client reads it as a SAS error. It is0now.lineage, usernav, refreshlibinfo (
1f54fad)These payloads were taken from the response the old service serves, so the comparison is against the real previous output rather than a transcription.
One correction since the first version of this PR: the captures had already lost the OMS and path separators -
OMSOBJ:SASLibrary\A59...written inside a template literal is the escape\A, so the old services servedOMSOBJ:SASLibraryA59...,D:MyDemoSankeyandBUILTINAdministrators.7d9d466restores seventeen of those values across five services (see the commit for the field list and the verification). Comparison against the served response cannot catch this class of defect, because the corruption is in the thing being compared against.Verification
Both trees deployed on one server (base
/Public/app/dc, branch/Public/app/dcnew) and every service POSTed to both. Parsed JSON compared with the envelope keys excluded. Every member is equal row for row except the nineteen separator-corrections described above (seventeen here, two inmetadetails).metanav.cy.tspasses against the converted services (1/1, 48s) on a seeded 3.13.0 mock estate.Not converted
lineage/fetchlineageis left alone deliberately. Its capture is not valid JSON - bare quotes inside the graphviz labels, raw newlines inside theSTRINGvalues, and a\Aescape that already ate a backslash - so the client cannot parse the response today. There is no working payload to preserve, and the intended text is a decision, not a rewrite. Nothing inclient/srcrequestslineage/fetchlineageeither, so it looks dead; either delete it or rebuild it, separately.sas/mocks/sas9/(targetsas9-mocks, 35 files) also still assigns_weboutdirectly and carries the same separator losses. Deliberately out of scope here so the review covers one tree; tracked as #463.The five raw-body services stay:
admin/exportdb,admin/makedata(HTML),public/getrawdata,public/getddl(own Content-Type and Content-Disposition), andauditors/getauditfile's browser fallback (a plain JSON document, not an envelope).webout()has no raw-body or header call.The four metanav services assigned a captured 2022 response to _webout, envelope and all, instead of using the runtime's webout() like every other service. The members are unchanged: metarepos outrepos 2 rows metaobjects objects 1 row metatypes types 71 rows metadetails attributes 12 rows associations 2 rows Both trees were deployed on one server, at /Public/app/dc and /Public/app/dcnew, and the responses compared as parsed JSON with the envelope keys excluded: outrepos, objects and types are equal row for row. metadetails changes in one place. The capture wrote the OMS URI as OMSOBJ:Property\A59..., but \A is an escape in a JavaScript template literal, so the backslash was consumed and the client received OMSOBJ:PropertyA59... - a URI that cannot resolve. The member now carries the backslash the SAS metadata server returns. The envelope comes from the runtime now, which also drops three artefacts of the recording: SYSCC "4" on metarepos (an error code from the captured day, which the client reads as a SAS error), SYSDATE/SYSTIME frozen at 26SEP22, and the SAS-only _METAUSER/_METAPERSON.feat(mocks): emit the metanav responses through webout()to feat(mocks): emit the captured-literal services through webout()The captures wrote values like OMSOBJ:SASLibrary\A59LNVZG.B500000K inside a template literal, where \A is an escape, so the separator never reached the client: it served OMSOBJ:SASLibraryA59LNVZG.B500000K. The same happened to D:\MyDemo\Sankey and BUILTIN\Administrators. Converting the service to webout() faithfully preserved the corrupted value - and the metadetails ASSOCURI, the one field fixed by hand at conversion time, was left as the only member that kept its separator, which is the inconsistency the review named. Seventeen values corrected across five services: lineage/fetchtablelineage info[0].LIBURI 1 lineage/getmetacols metacols[].COLURI 11 lineage/getmetatables PATH_SCHEMA, TABLEURI 2 public/refreshlibinfo libinfo[0].OWNERS 1 usernav/usermembersbyrole sasgroups[].URIMEM, sasmembers[].URIMEM 2 Verified by deploying both trees on one server and comparing the parsed responses: those seventeen fields are the only differences, and every other member of all thirteen converted services is unchanged (metadetails' two ASSOCURI, already carrying its separator, still differ from the capture for the same reason). Anything that splits a URI or a Windows path on the separator, or displays OWNERS or PATH_SCHEMA, now sees the value the SAS metadata server returns.Both findings are right, and the first one is worse than "the diff lost some escaping" - I propagated a corrupted value and then described it as preserved.
1. Separator loss - fixed in
7d9d466.Root cause: the captures wrote
OMSOBJ:SASLibrary\A59LNVZG.B500000Kinside a template literal, where\Ais the escapeA. The separator was already gone in the value the old service served -OMSOBJ:SASLibraryA59LNVZG.B500000K- so my two-deployment diff compared against corrupted data and called it equal.metadetailskept its separator only because I fixed that one by hand when converting it, which is exactly the inconsistency you spotted.Seventeen values corrected:
info[0].LIBURImetacols[].COLURIPATH_SCHEMA,TABLEURIlibinfo[0].OWNERSsasgroups[].URIMEM,sasmembers[].URIMEMPATH_SCHEMAisD:\MyDemo\SankeyandOWNERSisBUILTIN\Administrators; the OMS URIs take the separator before the repository id.Verified: both trees deployed on one server (
/Public/app/dcbase,/Public/app/dcnewbranch), all thirteen converted services POSTed to both, parsed JSON compared. Those seventeen fields plus the twometadetailsASSOCURI are the only differences; every other member of every service is unchanged.I have corrected the PR body, which claimed the escaped values kept their backslash - they did not, and the claim came from the same faulty comparison.
On assertions: there is no Cypress spec for the lineage, user, group, role or metadata screens, so nothing covers the separator either way - the deploy-diff is the guard. Worth its own change rather than smuggling a spec in here.
2.
sas/mocks/sas9/- a conscious call: out of scope for this PR. It is a separate target (sas9-mocks, appLoc/User Folders/sasdemo/), 35 files, and it needs its own deploy-and-diff against its own app location, so folding it in would double the blast radius of a review that is already about one tree. Tracked as #463 with the acceptance criteria and the note that it also carries the same separator losses.Merge note: this branch now depends on nothing outside itself; #462 (pin 3.14.0) is separate and independent.
REVIEWED ON 2026-11-04 13:39 UTC
Reviewed the diff - no blocking issues found.