I understand the basic sequence: glGenBuffers() creates a "buffer object name," glBindBuffer() creates the actual buffer object (as well as binding the name), glBufferData() "creates and initializes a buffer object's data store," and glVertexAttribPointer() sets the format of the buffer data, as well as saving the buffer object binding as part of vertex attrib array state.
The tricky thing that I'm unsure of is if the glVertexAttribPointer() stuff remains valid if the the "buffer object data store" (but not the buffer object itself) is blown away by a new call to glBufferData(). I'm aware that this isn't usually a good idea - use glBufferSubData() when just rewriting buffers - but glBufferData() seems like the only option if it needs to be resized. Plus, I'm just curious.
The reference pages make it sound like it could go either way - in particular (https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml):
If pointer is not NULL, a non-zero named buffer object must be bound to the GL_ARRAY_BUFFER target (see glBindBuffer), otherwise an error is generated. pointer is treated as a byte offset into the buffer object's data store. The buffer object binding (GL_ARRAY_BUFFER_BINDING) is saved as generic vertex attribute array state (GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING) for index index.
When a generic vertex attribute array is specified, size, type, normalized, stride, and pointer are saved as vertex array state, in addition to the current vertex array buffer object binding.
It seems like both the byte offset and the buffer object binding could remain valid, even if the data store was replaced by something different. But I could also see it being implemented such that at the time of the call, the binding captures a specific pointer into the data store, which becomes invalidated if the data store is replaced. Neither way seems to be precluded by the wording.