Gutenberg template lock but add/delete blocks programmatically

I couldn’t find a standard way to do this, but as I commented, an easy way to do it is by using a state which will store either the initial template you set or the inner blocks (i.e. their names and attributes) that were already added to the parent block. Which basically means, you will pass conditional template to the InnerBlocks component.

Working Example

  1. WordPress dependencies specific to this example, in addition to InnerBlocks etc.

    import { useSelect } from '@wordpress/data';
    import { useState } from '@wordpress/element';
    import { Button } from '@wordpress/components'; // for the demos; see below
    
  2. Now here’s the edit() function where I added 2 buttons for testing the functionality you wanted to have:

    function edit( { clientId } ) {
        const innerBlocks = useSelect( select => {
            const { getBlocks } = select( 'core/block-editor' );
            return getBlocks( clientId );
        }, [ clientId ] );
    
        // If there are no inner blocks yet, use the initial template. Else, we use
        // the name and attributes of each (immediate) child block.
        const _template = ( innerBlocks.length < 1 ) ? MY_TEMPLATE :
            innerBlocks.map( block => [ block.name, block.attributes ] );
    
        // Add a state which ensures React re-renders our block when the template is
        // changed.
        const [ template, setTemplate ] = useState( _template );
    
        // Demo callback which adds a child block.
        const demoInsertChildBlock = () => // wrapped
            setTemplate( [ ...template, [ 'core/quote', {} ] ] );
    
        // Demo callback which removes a child block.
        const demoRemoveChildBlock = () => {
            const newTemplate = [ ...template ]; // copy current template
            newTemplate.pop();                   // remove the last child
            setTemplate( newTemplate );
        };
    
        return (
            <div { ...useBlockProps() }>
                Hello from the editor!
    
                <InnerBlocks
                    template={ template }
                    templateLock="all"
                />
    
                <Button text="Insert demo block" variant="primary" onClick={ demoInsertChildBlock } />
                <span>&nbsp;</span>
                <Button text="Remove last block" variant="primary" onClick={ demoRemoveChildBlock } />
            </div>
        );
    }
    

    So as you could see, you just need to call setTemplate() function to add/remove a child block.