I found a bug, here is the code again:

0. if the current line contains hidden text, it shows the block
1. otherwise, the first tab indents, and the second tab hides
2. afterwards, it switches hide/show



(defun tab-hs-hide ()
  (interactive)
 
    (let ((obj (car (overlays-in
                     (save-excursion (move-beginning-of-line nil ) (point) )
                     (save-excursion (move-end-of-line nil) (point) ) ) ) ) )
      (cond ((and (null obj)
                  (eq last-command this-command) )
             (hs-hide-block) )
            ((and (overlayp obj)
                  (eq 'hs (overlay-get obj 'invisible)))
             (progn
               (move-beginning-of-line nil)
               (hs-show-block) ) )
            (t
             (funcall (lookup-key (current-global-map) (kbd "^I") ) ) ) ) ) )

(define-key hs-minor-mode-map [tab] 'tab-hs-hide )