Date: 2026-09-01 (continuation of the 2026-08-31 dry-run session) Author: Claude (Claude Code session) Requested by: Tom Tyler
Continuation of sync_jira_to_p4jobs.sh hardening, picking up after
SESSION_2026-08-31_first_dry_run.md (which found and fixed the
stdout-contamination silent-data-loss bug, change 33384). Tonight's work,
in order:
msg_green/msg_yellow/
msg_red), a LOGGING: section in -man documenting the self-logging
behavior, -D for full set -x tracing, get_old_log_timestamp()
inlined from the logging.lib equivalent, and moving the EXIT/SIGINT/
SIGTERM trap to the very first line of Main Program so config/lock
failures also complete through terminate() (previously they bypassed
it and skipped the final summary line). Also fixed a pre-existing
ShellCheck warning (dead pattern alternative in the status-mapping
case) while at it.msg()/dbg()/detail() themselves
to stderr -- correct, but "a bit extreme" for a two-call-site problem,
and risked complicating future color-output handling for every caller.
Reverted those shared functions to plain stdout and instead redirect
only the ~4 specific detail()/warnmsg() calls inside jira_get()
and map_jira_project_to_p4() (the two functions actually captured via
command substitution) directly to stderr (change 33388). Saved as a
general lesson in memory: prefer narrow, call-site-specific fixes over
changing widely-shared helpers.Limited: no (-max 5) summary line -- showing the
configured cap when it wasn't actually the limiting factor implied it
applied when it didn't (change 33389).declare Version="X.Y.Z" (semver) with every submit that
touches it -- Major/Minor/Patch per the usual rules. Caught up from the
untracked original 1.0.0 to 2.0.0 to reflect the accumulated
breaking changes already made this week (change 33386), then continued
bumping with each subsequent submit. Saved as a standing memory so this
isn't forgotten in future sessions.P4PasswdFile into a variable and passed it via
P4PASSWD. Tom asked to stop passing a ticket at all and instead
reference a P4CONFIG-format file. This went through two attempts:
p4 -config <path>. That flag does
not exist in this p4 client (verified directly against p4 -h and
p4 help environment -- P4CONFIG is only ever a filename p4
discovers via directory-walk, not an absolute-path flag). Caught this
myself before the first submit and switched to parsing
P4PORT/P4USER/P4TICKETS/P4TRUST out of the file and exporting
them per-call via env (this working version is what actually
shipped as change 33390 / v3.0.0) -- but several -man/README
example commands for one-time human setup still showed the
hallucinated p4 -config ... trust -y / login, which Tom caught
the next message ("Yikes, I see a hallucination in there").load_p4_config() now just exports
P4CONFIG="$P4ConfigFile" (an absolute P4CONFIG is used directly,
no directory-walk, and its values take precedence over the ambient
environment) and P4ENVIRO=/dev/null/.p4enviro (so no personal
~/.p4enviro leaks in). p4 itself then reads
P4PORT/P4USER/P4TICKETS/P4TRUST from the file and resolves the
ticket from P4TICKETS entirely on its own -- verified every piece
(absolute-path P4CONFIG behavior, P4ENVIRO override, the
TicketExpiration ztag field and its not-logged-in empty-string case)
against the real p4 client before writing any code, this time.P4PORT/P4USER
itself at all anymore -- every p4 call picks them up from
P4CONFIG directly -- and load_p4_config() doesn't validate the
file's contents beyond confirming it exists; any misconfiguration
surfaces naturally from the preflight checks that follow, rather than
this script re-implementing p4's own validation.P4PORT from ssl:workshop.perforce.com:1666
to public.perforce.com:1666 -- the Public Depot isn't actually
SSL-encrypted (it's a public, read-visible server) -- and changed
StateDir's default from ${P4HOME:-/p4/1}/tmp/jira_sync to
${P4TMP:-/tmp}/jira_sync (same value on a standard SDP instance-1
host, cleaner fallback off SDP).check_p4_login() (change 33391): verifies the configured P4USER has
a ticket with at least 60 minutes remaining, via
p4 -ztag -F '%TicketExpiration%' login -s. An empty result means not
logged in at all.check_p4_access() (change 33393): verifies p4 protects -m reports
admin or super -- confirmed directly via p4 help job that
-f (which this script always uses, to set
ReportedDate/ModifiedBy/ModifiedDate) requires admin.Tom set up a dedicated service account, bot_p4jira, with its own
P4TICKETS file, and asked directly: have we pushed anything live yet, and
do we think it'll work? Answer: no live runs yet, everything has been
-C ... -max N -v N with no -y. Checked three things live against the
Public Depot (all read-only, zero risk) to answer honestly rather than
guess:
p4 jobspec -o: real field names/values match exactly what the script
writes (Status/Type enums, perforce-software-sdp present in the
Project enum).p4 job -o SDP-1172: came back as a blank template, confirming most/all
of a first live run would be creating jobs, not updating existing
ones -- consistent with the tool's whole premise (Public Depot jobs
stopped tracking JIRA a while back).p4 protects -m: initially reported write, not admin -- which would
have made every live job upsert fail on the -f flag. Tom granted
admin access mid-session; re-checked and confirmed admin. This is
exactly the gap check_p4_access() (above) now catches automatically.Net: as of this session, the identified live-readiness blocker (insufficient P4 access) has been resolved and both new preflight checks pass. No live run has been attempted; Tom deferred that to a future session.
-batch as a separate option from -maxTom observed that -max currently always blocks watermark advancement
(true, by design -- it exists as a pilot/safety-valve cap: touch a few real
issues, and if anything's wrong, rerun the exact same ones after fixing
config rather than skipping past unreviewed data). He raised a second,
different use case: processing a large backlog in controlled batches (e.g.
50 at a time) where the watermark should advance after each clean batch,
making forward progress across repeated invocations. Proposed handling this
as a new -batch option, mutually exclusive with -max, rather than
changing -max's existing behavior. Captured here, not implemented yet.
Three things worth working out before building it:
-max) so the
existing pilot-cap safety property doesn't quietly change for anyone
already using -max that way.RunStartTime (everything up to "now" has been
verified). A batch-capped run has only verified issues up to the
updated timestamp of the last issue actually processed (in the
ORDER BY updated ASC stream) -- advancing to "now" would skip
unverified issues. -batch mode would need to track and persist that
last-processed timestamp instead of reusing the existing
RunStartTime-based logic.JiraProjects entry. If a batch cap cuts the run short partway through
project B, with project C not yet even queried this run, advancing the
shared watermark to B's cutoff point would silently skip project C's
entire unprocessed backlog between the old watermark and that cutoff --
C's own progress was never actually verified this run. Safest options:
(a) only support -batch when a single project is targeted via -p
(much smaller, safer first step), or (b) move to a per-project
watermark store (bigger change -- the watermark file becomes keyed per
project). Lean toward (a) if/when this gets built.-batch option (above) -- design captured, not implemented.curl --user argv exposure in jira_get(), symmetric to the P4
ticket exposure already fixed -- still open, noted in
SESSION_2026-08-30_dry_run_and_next_steps.md.jira_type_to_p4() "Feature Request" mapping gap -- JIRA's actual
"Feature Request" type falls through to the generic Problem default
rather than Feature, since the case pattern only matches "new feature"*. Still open, noted in SESSION_2026-08-31_first_dry_run.md.p4 fix / P4Blog idea -- filed as job J2J-1 on the Public
Depot server directly (no JIRA project for j2j itself): "Enhance to
translate P4Blog from JIRA to P4 fixes." Design questions from the
original session log
(SESSION_2026-08-30_dry_run_and_next_steps.md) still apply.bot_p4jira has admin access; both new preflight checks pass), but
deliberately deferred. Recommended starting point when ready: smallest
possible blast radius, e.g. -y -max 1, before removing the cap.# Session Log: P4 Credential Redesign, New Preflight Checks, Live-Readiness Check
**Date:** 2026-09-01 (continuation of the 2026-08-31 dry-run session)
**Author:** Claude (Claude Code session)
**Requested by:** Tom Tyler
---
## Context
Continuation of `sync_jira_to_p4jobs.sh` hardening, picking up after
`SESSION_2026-08-31_first_dry_run.md` (which found and fixed the
stdout-contamination silent-data-loss bug, change 33384). Tonight's work,
in order:
1. Aligned further with the SDP Bash Coding Standard and its template.sh
(change 33383): colorized status output (`msg_green`/`msg_yellow`/
`msg_red`), a `LOGGING:` section in `-man` documenting the self-logging
behavior, `-D` for full `set -x` tracing, `get_old_log_timestamp()`
inlined from the logging.lib equivalent, and moving the `EXIT`/`SIGINT`/
`SIGTERM` trap to the very first line of Main Program so config/lock
failures also complete through `terminate()` (previously they bypassed
it and skipped the final summary line). Also fixed a pre-existing
ShellCheck warning (dead pattern alternative in the status-mapping
`case`) while at it.
2. **Scaled back the stdout/stderr fix per feedback.** The original fix for
the stdout-contamination bug moved `msg()`/`dbg()`/`detail()` themselves
to stderr -- correct, but "a bit extreme" for a two-call-site problem,
and risked complicating future color-output handling for every caller.
Reverted those shared functions to plain stdout and instead redirect
only the ~4 specific `detail()`/`warnmsg()` calls inside `jira_get()`
and `map_jira_project_to_p4()` (the two functions actually captured via
command substitution) directly to stderr (change 33388). Saved as a
general lesson in memory: prefer narrow, call-site-specific fixes over
changing widely-shared helpers.
3. Fixed a misleading `Limited: no (-max 5)` summary line -- showing the
configured cap when it wasn't actually the limiting factor implied it
applied when it didn't (change 33389).
4. **Established a version-bump policy**, per Tom's request: bump the
script's `declare Version="X.Y.Z"` (semver) with every submit that
touches it -- Major/Minor/Patch per the usual rules. Caught up from the
untracked original `1.0.0` to `2.0.0` to reflect the accumulated
breaking changes already made this week (change 33386), then continued
bumping with each subsequent submit. Saved as a standing memory so this
isn't forgotten in future sessions.
5. **Replaced the P4 ticket-handling mechanism entirely.** Previously the
script read a `P4PasswdFile` into a variable and passed it via
`P4PASSWD`. Tom asked to stop passing a ticket at all and instead
reference a `P4CONFIG`-format file. This went through two attempts:
- **First attempt (wrong):** tried `p4 -config <path>`. That flag does
not exist in this `p4` client (verified directly against `p4 -h` and
`p4 help environment` -- `P4CONFIG` is only ever a *filename* p4
discovers via directory-walk, not an absolute-path flag). Caught this
myself before the first submit and switched to parsing
`P4PORT`/`P4USER`/`P4TICKETS`/`P4TRUST` out of the file and exporting
them per-call via `env` (this working version is what actually
shipped as change 33390 / v3.0.0) -- but several `-man`/README
*example* commands for one-time human setup still showed the
hallucinated `p4 -config ... trust -y` / `login`, which Tom caught
the next message ("Yikes, I see a hallucination in there").
- **Second attempt (correct, change 33391 / v3.1.0):** per Tom's
specific direction, `load_p4_config()` now just exports
`P4CONFIG="$P4ConfigFile"` (an absolute `P4CONFIG` is used directly,
no directory-walk, and its values take precedence over the ambient
environment) and `P4ENVIRO=/dev/null/.p4enviro` (so no personal
`~/.p4enviro` leaks in). `p4` itself then reads
`P4PORT`/`P4USER`/`P4TICKETS`/`P4TRUST` from the file and resolves the
ticket from `P4TICKETS` entirely on its own -- verified every piece
(absolute-path `P4CONFIG` behavior, `P4ENVIRO` override, the
`TicketExpiration` ztag field and its not-logged-in empty-string case)
against the real `p4` client before writing any code, this time.
- Per further feedback, the script doesn't track `P4PORT`/`P4USER`
itself at all anymore -- every `p4` call picks them up from
`P4CONFIG` directly -- and `load_p4_config()` doesn't validate the
file's contents beyond confirming it exists; any misconfiguration
surfaces naturally from the preflight checks that follow, rather than
this script re-implementing p4's own validation.
- Also fixed the example `P4PORT` from `ssl:workshop.perforce.com:1666`
to `public.perforce.com:1666` -- the Public Depot isn't actually
SSL-encrypted (it's a public, read-visible server) -- and changed
`StateDir`'s default from `${P4HOME:-/p4/1}/tmp/jira_sync` to
`${P4TMP:-/tmp}/jira_sync` (same value on a standard SDP instance-1
host, cleaner fallback off SDP).
6. **Added two new preflight checks**, both bailing with a clear message
before any syncing starts rather than letting every job upsert fail
individually:
- `check_p4_login()` (change 33391): verifies the configured P4USER has
a ticket with at least 60 minutes remaining, via
`p4 -ztag -F '%TicketExpiration%' login -s`. An empty result means not
logged in at all.
- `check_p4_access()` (change 33393): verifies `p4 protects -m` reports
`admin` or `super` -- confirmed directly via `p4 help job` that
`-f` (which this script always uses, to set
`ReportedDate`/`ModifiedBy`/`ModifiedDate`) requires `admin`.
## Live-readiness check
Tom set up a dedicated service account, `bot_p4jira`, with its own
`P4TICKETS` file, and asked directly: have we pushed anything live yet, and
do we think it'll work? Answer: no live runs yet, everything has been
`-C ... -max N -v N` with no `-y`. Checked three things live against the
Public Depot (all read-only, zero risk) to answer honestly rather than
guess:
- `p4 jobspec -o`: real field names/values match exactly what the script
writes (`Status`/`Type` enums, `perforce-software-sdp` present in the
`Project` enum).
- `p4 job -o SDP-1172`: came back as a blank template, confirming most/all
of a first live run would be *creating* jobs, not updating existing
ones -- consistent with the tool's whole premise (Public Depot jobs
stopped tracking JIRA a while back).
- `p4 protects -m`: initially reported `write`, not `admin` -- which would
have made every live job upsert fail on the `-f` flag. Tom granted
`admin` access mid-session; re-checked and confirmed `admin`. This is
exactly the gap `check_p4_access()` (above) now catches automatically.
**Net: as of this session, the identified live-readiness blocker (insufficient
P4 access) has been resolved and both new preflight checks pass. No live run
has been attempted; Tom deferred that to a future session.**
## Design note for later: `-batch` as a separate option from `-max`
Tom observed that `-max` currently always blocks watermark advancement
(true, by design -- it exists as a pilot/safety-valve cap: touch a few real
issues, and if anything's wrong, rerun the exact same ones after fixing
config rather than skipping past unreviewed data). He raised a second,
different use case: processing a large backlog in controlled batches (e.g.
50 at a time) where the watermark *should* advance after each clean batch,
making forward progress across repeated invocations. Proposed handling this
as a new `-batch` option, mutually exclusive with `-max`, rather than
changing `-max`'s existing behavior. Captured here, not implemented yet.
Three things worth working out before building it:
1. **Needs to be a distinct, explicit flag** (not a mode of `-max`) so the
existing pilot-cap safety property doesn't quietly change for anyone
already using `-max` that way.
2. **The watermark-advance target differs from a normal full run.** A full
clean run advances to `RunStartTime` (everything up to "now" has been
verified). A batch-capped run has only verified issues up to the
`updated` timestamp of the last issue actually processed (in the
`ORDER BY updated ASC` stream) -- advancing to "now" would skip
unverified issues. `-batch` mode would need to track and persist that
last-processed timestamp instead of reusing the existing
`RunStartTime`-based logic.
3. **Multi-project runs make this a real correctness risk, not just an
edge case.** The watermark is one shared value across every configured
`JiraProjects` entry. If a batch cap cuts the run short partway through
project B, with project C not yet even queried this run, advancing the
shared watermark to B's cutoff point would silently skip project C's
entire unprocessed backlog between the old watermark and that cutoff --
C's own progress was never actually verified this run. Safest options:
(a) only support `-batch` when a single project is targeted via `-p`
(much smaller, safer first step), or (b) move to a per-project
watermark store (bigger change -- the watermark file becomes keyed per
project). Lean toward (a) if/when this gets built.
## Outstanding items (not done this session)
1. **`-batch` option** (above) -- design captured, not implemented.
2. **`curl --user` argv exposure** in `jira_get()`, symmetric to the P4
ticket exposure already fixed -- still open, noted in
`SESSION_2026-08-30_dry_run_and_next_steps.md`.
3. **`jira_type_to_p4()` "Feature Request" mapping gap** -- JIRA's actual
"Feature Request" type falls through to the generic `Problem` default
rather than `Feature`, since the case pattern only matches `"new
feature"*`. Still open, noted in `SESSION_2026-08-31_first_dry_run.md`.
4. **`p4 fix` / `P4Blog` idea** -- filed as job **J2J-1** on the Public
Depot server directly (no JIRA project for j2j itself): "Enhance to
translate P4Blog from JIRA to P4 fixes." Design questions from the
original session log
(`SESSION_2026-08-30_dry_run_and_next_steps.md`) still apply.
5. **First live-fire test** -- readiness blockers now cleared
(`bot_p4jira` has `admin` access; both new preflight checks pass), but
deliberately deferred. Recommended starting point when ready: smallest
possible blast radius, e.g. `-y -max 1`, before removing the cap.
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 33523 | C. Thomas Tyler | Renamed folder. | ||
| //j2j/dev/ai/SESSION_2026-09-01_p4_credentials_and_preflight_checks.md | |||||
| #1 | 33394 | C. Thomas Tyler | Session log: P4 credential handling redesign (P4CONFIG/P4ENVIRO export, catching and fixing a p4 -config hallucination along the way), new check_p4_login()/check_p4_access() preflight checks, live-readiness check (jobspec fields confirmed, access level gap found and resolved), and design notes for a future -batch option distinct from -max. | ||