Fix: XACA-0516 — Consolidate KANBAN_BACKUP_INTERVAL default to a shared constant#33
Conversation
Introduces libexec/lib/constants.sh as the single source of truth for shell
constants shared across the tap. Starts with KANBAN_BACKUP_INTERVAL_DEFAULT=900
(15-minute kanban backup cadence). Uses ${VAR:=default} so the file is safe to
source twice and env-var overrides still win, then marks the value readonly.
Eliminates the sibling-drift pattern that XACA-0510 and XACA-0512 worked
around with paired "change one, change the other" NOTE comments. See
XACA-0510-013 (review follow-up from PR #30).
Subitem 001 of XACA-0516. Consumers will be migrated to source this file in
subitems 002 (install-kanban.sh), 003 (aiteamforge-upgrade.sh), and 004
(aiteamforge-migrate.sh).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…_DEFAULT
install-kanban.sh now sources libexec/lib/constants.sh and resolves
KANBAN_BACKUP_INTERVAL via ${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT}
instead of the hardcoded literal 900. Side-effect: the value now respects an
env-var override (it was previously a hard-set), which matches how
aiteamforge-upgrade.sh and aiteamforge-migrate.sh already treat it.
The template substitution at line 773 (--BACKUP_INTERVAL placeholder) is
unchanged — it consumes the same variable name and works regardless of how
the value got there.
Subitem 002 of XACA-0516.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
aiteamforge-upgrade.sh now sources libexec/lib/constants.sh and consumes
$KANBAN_BACKUP_INTERVAL_DEFAULT directly. The local mirror constant
XACA_0510_KANBAN_BACKUP_INTERVAL_DEFAULT and its three-line "change one,
change the other" NOTE comment are deleted — the whole reason that NOTE
existed (sibling-drift between upgrade.sh and install-kanban.sh) is now
structurally impossible because both files read the same source of truth.
The consumer line in _render_launchagent_template still resolves via
${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT}, so env-var
override semantics are preserved.
Subitem 003 of XACA-0516.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Picks up XACA-0512 (migrate.sh launchagent render rework) and its test fixture — both were merged to main after this feature branch was cut. This brings aiteamforge-migrate.sh to the version that contains the XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT mirror, which subitem 004 will now remove. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
aiteamforge-migrate.sh now sources libexec/lib/constants.sh and consumes $KANBAN_BACKUP_INTERVAL_DEFAULT directly. The local mirror constant XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT is deleted and the NOTE comment is trimmed to mention only FLEET_MONITOR_PORT (which retains its sibling-drift pattern and is captured as a follow-up — explicitly out of scope here). XACA_0512_FLEET_MONITOR_PORT_DEFAULT and its consumer remain untouched so this commit stays scoped to the kanban-backup-interval consolidation. Subitem 004 of XACA-0516. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Captures the four migration commits (constants.sh + three consumers) under a single Unreleased entry, documents the sibling-drift pattern eliminated, calls out the preserved override semantics, lists the test results (159/159 across six files plus an explicit default-path test), and flags the FLEET_MONITOR_PORT follow-up as out of scope. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
XACA-0516 Code Review — PR #33
Verdict
APPROVE
Security
No concerns. No secrets, tokens, or API keys introduced. No shell injection vectors — constants.sh contains no eval, no subshell expansion, no user-controlled input. The readonly enforcement is real: verified that assigning to KANBAN_BACKUP_INTERVAL_DEFAULT after sourcing exits non-zero under set -e (bash: KANBAN_BACKUP_INTERVAL_DEFAULT: readonly variable).
Architecture / Scope
Clean. libexec/lib/constants.sh is the correct home — it sits alongside common.sh, config.sh, and the other lib files. All three consumer scripts use the identical ${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT} resolution pattern — no call-site drift. Mirror constants (XACA_0510_KANBAN_BACKUP_INTERVAL_DEFAULT, XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT) are confirmed absent from libexec/ (grep clean). The FLEET_MONITOR_PORT_DEFAULT sibling is correctly left untouched per the PR description — separate ticket, correct call. KANBAN_BACKUP_INTERVAL_DEFAULT appears in exactly 5 places under libexec/: the 2-line definition block in constants.sh plus the 3 consumer sites. The literal 900 appears in exactly 1 file.
Code Quality
The : "${VAR:=default}" + readonly VAR idiom is correct and safe under set -euo pipefail. Verified via live test: double-sourcing is idempotent (no re-declaration error), env-var overrides set before sourcing are preserved by :=, and readonly blocks post-source reassignment. Source ordering in upgrade.sh and migrate.sh follows common.sh → config.sh → constants.sh — sensible. install-kanban.sh skips config.sh (pre-existing, not introduced by this PR). constants.sh is standalone — no source calls, no dependencies on other lib files. All four touched files parse cleanly under bash -n. The NOTE comment in migrate.sh is correctly trimmed to reference only FLEET_MONITOR_PORT_DEFAULT, which is the remaining sibling.
Behavioral Delta
install-kanban.sh previously hard-set KANBAN_BACKUP_INTERVAL=900 unconditionally, so a pre-set env override would be silently ignored. This PR changes that to ${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT}, making the installer consistent with upgrade.sh and migrate.sh which already honored the override. The change is intentional, clearly documented in the CHANGELOG, and the rendered plist value defaults to 900 in all three paths — no behavioral regression for standard installs.
Empty-string semantics: ${VAR:-DEFAULT} falls through to default when VAR="". This is the intended behavior (verified). The PR description acknowledges it; test coverage includes an explicit case. A non-blocking suggestion to document this in constants.sh itself has been filed as a subitem.
Testing
PR claims 159/159 tests pass across six test files. The QA tester gate will independently confirm. The PR description also notes an explicit default-path test covering 5 cases (default-resolves, override-wins, empty-string-falls-through, double-source idempotent, readonly-blocked) — this is good coverage for the new constant contract.
Non-blocking suggestions (filed as kanban subitems)
tests/test-xaca-0512-migrate-launchagent-render.sh:83— staleXACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT=900declaration is dead weight. Cleanup candidate for theFLEET_MONITOR_PORTfollow-up ticket.libexec/lib/constants.sh— consider adding a one-liner comment explaining that:-semantics means an empty-stringKANBAN_BACKUP_INTERVAL=""also falls through to the default, so readers don't wonder if a blank plist entry is possible.
Files reviewed
libexec/lib/constants.sh(new) — 17 lineslibexec/installers/install-kanban.sh— source line added + hard-set replaced with override-respecting expressionlibexec/commands/aiteamforge-upgrade.sh— source line added + local mirror constant + NOTE removed, render function updatedlibexec/commands/aiteamforge-migrate.sh— source line added + local KANBAN mirror constant + partial NOTE removed, render function updated; FLEET_MONITOR_PORT mirror correctly retainedCHANGELOG.md— Unreleased section, XACA-0516 entry added above XACA-0512
Notes
The stale XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT at tests/test-xaca-0512-migrate-launchagent-render.sh:83 is confirmed, acknowledged in the PR description as out-of-scope, and filed as a [Review] subitem. It is not a merge blocker — the declaration is unused in that test file and does not affect test execution. The PR is well-scoped, the idiom is correct, and the sibling-drift elimination is complete for the KANBAN_BACKUP_INTERVAL family.
There was a problem hiding this comment.
XACA-0516 QA Test Gate — Result
Test Suite Results
test-xaca-0510-launchagent-render.shvia test-runner.sh: PASS (15/15)test-xaca-0512-migrate-launchagent-render.shvia test-runner.sh: PASS (17/17)test-installers.sh: 22/32 — 10 PRE-EXISTING failures (XACA-0212 kb-quarantine-stub, identical on main)test-migration.sh: PASS (18/18)test-lifecycle.sh: PASS (22/22)test-board-template.sh: PASS (55/55)
Note on test-installers.sh: All 10 failures are tagged XACA-0212 and report command not found: kb-quarantine-stub. Confirmed identical failure count (22/32) on main prior to this PR. This PR introduced no regressions.
Behavior Verification
- Default path (env unset → 900): PASS
- Override path (env=1800 → 1800): PASS
- Empty-string path (env="" → 900): PASS
- Readonly enforcement (variable appears in
readonly -p; reassignment exits 1): PASS
Lint / Syntax
bash -non 4 modified files: PASS (all OK)shellcheckbaseline drift: No new warnings introduced. Each consumer script gained one expected SC1091 (info) warning for the newsource ".../constants.sh"line — consistent with the pre-existing SC1091 pattern for all other sourced libs.constants.shitself is clean (zero warnings).
Structural Verification
libexec/lib/constants.shexists with: "${KANBAN_BACKUP_INTERVAL_DEFAULT:=900}"+readonlypattern: CONFIRMED- All 3 consumer scripts source
constants.sh: CONFIRMED - All 3 consumer scripts use
${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT}(override-respecting form): CONFIRMED XACA_0510_KANBAN_BACKUP_INTERVAL_DEFAULTsymbol gone from all files: CONFIRMEDXACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULTorphan attests/test-xaca-0512-migrate-launchagent-render.sh:83: CONFIRMED present but not-a-defect (test-local constant never consumed by production scripts; confirmed pre-existing and flagged as known dead weight in PR)900literal appears ONLY inconstants.shamong the 4 modified files: CONFIRMEDinstall-kanban.sh:774plist substitution uses$KANBAN_BACKUP_INTERVAL(already-resolved value from line 17): CONFIRMED
Notes
The orphan readonly XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT=900 declaration at test-xaca-0512-migrate-launchagent-render.sh:83 is not a defect — it is a test-local constant that was never consumed by any production script. The PR author has flagged it as known dead weight. It poses no behavioral risk, but a follow-up cleanup would eliminate the last _0512_-prefixed symbol from the codebase.
Verdict
APPROVE — all production behavior verification tests pass, all meaningful regressions pass, and the pre-existing test-installers.sh failures (10, XACA-0212) are confirmed to pre-date this PR on main.
Two follow-up cleanups from the code reviewer:
1. tests/test-xaca-0512-migrate-launchagent-render.sh — remove the stale
`readonly XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT=900` declaration.
It was a test-local mirror of the production constant that XACA-0516
already deleted. Every call site injects KANBAN_BACKUP_INTERVAL="900"
as an env override into the eval'd migrate.sh functions, so the
declaration was declared-but-never-read dead weight. The companion
FLEET_MONITOR_PORT_DEFAULT mirror in the same fixture is intentionally
left for the FLEET_MONITOR_PORT consolidation follow-up.
2. libexec/lib/constants.sh — document the ${VAR:-default} empty-string
fallthrough semantics that all three consumers rely on. Operators
setting KANBAN_BACKUP_INTERVAL='' will get 900, not a blank plist
entry. The comment also points readers at the override pattern
consumers use and gives a concrete override example.
Addresses XACA-0516-011 and XACA-0516-012 (PR #33 review subitems).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
XACA-0516 QA Test Gate — Pass 2 (against HEAD f59db29)
This pass re-runs the suite against the new HEAD f59db29 which added the
review-cleanup commit on top of the previously-approved af8d4d7. Prior
APPROVE was explicitly against the older commit and is stale.
Diff verification
- Only the two expected files changed between
af8d4d7andf59db29: PASS constants.shcomment paragraph present: PASS (line 18,:-operator semantics documented)- Stale
XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULTdeclaration removed: PASS XACA_0512_FLEET_MONITOR_PORT_DEFAULTstill present (untouched): PASS (line 83)
Test Suite (via test-runner.sh)
test-xaca-0510-launchagent-render.sh: 15/15 PASS (exit 0)test-xaca-0512-migrate-launchagent-render.sh: 17/17 PASS (exit 0)test-migration.sh: 18/18 PASS (exit 0)test-lifecycle.sh: 22/22 PASS (exit 0)test-board-template.sh: 55/55 PASS (exit 0)- Total: 127 tests, 0 failures
- (Note:
test-installers.shskipped this pass — pre-existing XACA-0212 failures unchanged from pass 1)
Behavior verification
- Default path: PASS (unset KANBAN_BACKUP_INTERVAL resolves to 900)
- Override path: PASS (KANBAN_BACKUP_INTERVAL=1800 preserved through sourcing)
- Empty-string fallthrough: PASS (KANBAN_BACKUP_INTERVAL='' correctly falls through to 900)
- Readonly enforcement: PASS (readonly error fires on stderr; subshell output-capture suppresses
it in the test harness but the error is confirmed present on the outer shell's stderr —
same behavior as pass 1, expected)
Lint / Syntax
bash -n libexec/lib/constants.sh: PASSshellcheck libexec/lib/constants.sh: PASS (no warnings)
Verdict
APPROVE — The f59db29 commit applies exactly the two described changes with no
regressions. All 127 tests pass. Behavior semantics confirmed correct. The
cleanup is minimal, targeted, and logically sound.
There was a problem hiding this comment.
XACA-0516 Code Review — Pass 2 (against HEAD f59db29)
This pass re-reviews the PR after the f59db29 cleanup commit that addressed
my two prior [Review] suggestions inline. Prior APPROVE was against
af8d4d7 and is stale.
Delta review — f59db29 only
- Only the two expected files changed: PASS
tests/test-xaca-0512-migrate-launchagent-render.sh: staleXACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULTdeclaration removed cleanly; its paired SC2034 disable comment removed with it; FLEET_MONITOR mirror untouched at line 83: PASSlibexec/lib/constants.sh: new 6-line comment paragraph accurately documents${VAR:-default}empty-string fallthrough semantics, placed correctly between the consumers list and the: "${...}"line: PASS
Original review concerns (now addressed)
- XACA-0516-011 (stale test fixture declaration): RESOLVED —
XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULTand its orphaned SC2034 comment are gone; no leftover debris; test still passes 17/17. - XACA-0516-012 (constants.sh empty-string comment): RESOLVED — comment correctly describes
:-semantics (unset OR empty falls through), distinguishes from:=(initialization), and provides a concrete override example (KANBAN_BACKUP_INTERVAL=1800). The wording matches what consumers actually do.
Lint / Syntax
bash -nonlibexec/lib/constants.sh: PASSbash -nontests/test-xaca-0512-migrate-launchagent-render.sh: PASSshellcheck libexec/lib/constants.sh: PASS — zero new warnings- Test suite (test-runner.sh): 17/17 PASS, exit 0
Verdict
APPROVE — both [Review] subitems addressed accurately and completely. No new concerns introduced by the delta.
Summary
Three production scripts under
libexec/each carried a hardcodedKANBAN_BACKUP_INTERVAL=900(or a ticket-prefixed mirror likeXACA_0510_KANBAN_BACKUP_INTERVAL_DEFAULT) with hand-written "if you change one, change the other" NOTE comments — the classic sibling-drift smell. This PR addslibexec/lib/constants.shas the single source of truth and migrates all three call sites to consume${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT}from it.Originated as the XACA-0510-013 review follow-up subitem from PR #30; XACA-0512 inherited the same pattern. This ticket consolidates all three sites.
Changes Made
libexec/lib/constants.sh(NEW)Single source of truth for shared shell constants. Uses
: "${VAR:=default}"+readonlyso the file is safe to source twice, env-var overrides set before sourcing still win, and the value cannot be mutated after the fact.libexec/installers/install-kanban.shSources
lib/constants.sh. LiteralKANBAN_BACKUP_INTERVAL=900→${KANBAN_BACKUP_INTERVAL:-$KANBAN_BACKUP_INTERVAL_DEFAULT}. Side-effect: the value now respects an env-var override (it was previously a hard-set), matching how upgrade.sh and migrate.sh already treated it.libexec/commands/aiteamforge-upgrade.shSources
lib/constants.sh. Deletes the local mirror constantXACA_0510_KANBAN_BACKUP_INTERVAL_DEFAULTand its three-line "change one, change the other" NOTE comment. The render function consumes$KANBAN_BACKUP_INTERVAL_DEFAULTdirectly.libexec/commands/aiteamforge-migrate.shSources
lib/constants.sh. Deletes the localXACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULTmirror. NOTE comment trimmed to mention onlyFLEET_MONITOR_PORT_DEFAULT(the next sibling-drift consolidation candidate, scoped to its own follow-up).CHANGELOG.mdSingle consolidated Unreleased entry documenting the consolidation.
Impact
5 files (1 new + 4 modified). ~30 net lines changed (additions for the new constants file and source lines; deletions of the mirror declarations and NOTE comments).
Risk
LOW. Default value unchanged (900 seconds), env-var override semantics preserved everywhere, test fixtures untouched. The only behavioral delta is install-kanban.sh now respects the override env var (was hard-set) — strictly an improvement and aligned with the other two scripts.
Testing
All six relevant test files pass: 159/159 tests.
test-xaca-0510-launchagent-render.shtest-xaca-0512-migrate-launchagent-render.shtest-installers.shtest-migration.shtest-lifecycle.shtest-board-template.shPlus an explicit default-path test (5 cases) — the existing fixtures only exercise the override path:
900KANBAN_BACKUP_INTERVAL=1800→1800KANBAN_BACKUP_INTERVAL=→900constants.shtwice does not errorKANBAN_BACKUP_INTERVAL_DEFAULTafter sourcing failsFor QA — test invocation gotcha:
test-xaca-0510-*andtest-xaca-0512-*files MUST be invoked viabash tests/test-runner.sh <file>— running them directly bypassessetup_test_envand produces misleading failures. The runner is the required entrypoint.Breaking Changes
None.
Checklist
bash -nclean on all four modified files)libexec/lib/is the established home for shared shell libs alongsidecommon.sh,config.sh, etc.)Out of Scope (follow-up candidates)
aiteamforge-migrate.shalso carriesXACA_0512_FLEET_MONITOR_PORT_DEFAULT=3000mirroringinstall-fleet-monitor.sh:15. Same sibling-drift pattern, deferred to a separate ticket so this PR stays scoped.tests/test-xaca-0512-migrate-launchagent-render.sh:83retains a staleXACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULTdeclaration discovered by the sweep — dead weight, doesn't affect tests, cleanup candidate.Test plan for reviewers
bash tests/test-runner.sh test-xaca-0510-launchagent-render.sh→ 15/15 PASSbash tests/test-runner.sh test-xaca-0512-migrate-launchagent-render.sh→ 17/17 PASSbash -n libexec/lib/constants.sh libexec/installers/install-kanban.sh libexec/commands/aiteamforge-upgrade.sh libexec/commands/aiteamforge-migrate.sh→ all cleangrep -rn 'XACA_0510_KANBAN_BACKUP_INTERVAL_DEFAULT\|XACA_0512_KANBAN_BACKUP_INTERVAL_DEFAULT' libexec/→ no hitsgrep -rn 'KANBAN_BACKUP_INTERVAL_DEFAULT' libexec/→ exactly one definition (constants.sh) + three consumersgrep -rn '\b900\b' libexec/→ only constants.sh🤖 Generated with Claude Code