Skip to content

cache: allow underscore in instance keys / resource names (MON-5369 §5 generalization)#21

Merged
rianjs merged 1 commit into
mainfrom
feature/MON-5369-cache-allow-underscore
May 19, 2026
Merged

cache: allow underscore in instance keys / resource names (MON-5369 §5 generalization)#21
rianjs merged 1 commit into
mainfrom
feature/MON-5369-cache-allow-underscore

Conversation

@rianjs
Copy link
Copy Markdown
Contributor

@rianjs rianjs commented May 19, 2026

Second prerequisite (PR-A2) for MON-5369. Surfaced while porting jtk's cache: cli-common/cache's safeComponent regex rejected _, but jtk's original cache imposed no charset restriction on resource names — so a resource named with an underscore (valid in jtk forever) would start erroring through the facade.

Change (purely permissive)

safeComponent: ^[A-Za-z0-9][A-Za-z0-9.\-]*$^[A-Za-z0-9][A-Za-z0-9._\-]*$. Underscore is filesystem-safe and not a path separator. No previously-valid input becomes invalid (permissive widening), and the traversal (..), path-separator (/ \), and trailing-dot guards are unchanged — regression-tested. Leading _ still rejected (must start alphanumeric).

This is the §5 release-train "first port surfaces a tier-1 generalization" case (additive/looser ⇒ no coordinated-consumer break; repin follows).

Tests

a_b/host_1/also_ok round-trip through WriteResource/ReadResource; widening still rejects a/b, a\b, .., a..b, ../x, trailing., _leading. make check green (lint 0, -race all packages incl. credstore); go.mod/go.sum byte-identical.

Review note

Architect session 019e4019 (Codex quota temporarily exhausted; per workstream policy it doesn't block) will review this with PR-B (jtk facade) once quota resets.

[MON-5369]

Second §5 first-port generalization (MON-5369). cli-common/cache's
component guard rejected "_", but the first consumer (jtk) imposed NO
charset limit on resource names — a resource like "also_ok" was valid
there and must stay valid through the facade. Underscore is
filesystem-safe and not a separator, so widening the allowed set is
purely permissive: no previously-valid input becomes invalid, and the
traversal / path-separator / trailing-dot guards are unchanged
(regression-tested). Benefits every future consumer whose resource or
instance identifiers use "_".

[MON-5369]
@rianjs rianjs merged commit e67b2fc into main May 19, 2026
4 checks passed
@rianjs rianjs deleted the feature/MON-5369-cache-allow-underscore branch May 19, 2026 13:43
rianjs added a commit that referenced this pull request May 20, 2026
…ore-close [MON-5375] (#22)

* docs(state): finalize §6 step 6 + tier-2 deferral for INT-310 tag-before-close [MON-5375]

Adds §6.6 close-out retrospective: 5 of 6 port units shipped (jtk-cache,
Atlassian shared config, gro, slck, nrq); sfdc parked and explicitly
excluded from the v0.1.0 train (no cli-common dependency today).

Commons API additions during the rollout: cache.WriteEnvelope (#20) and
underscore in the cache-component regex (#21). Both additive; no
breaking change required across 5 ports.

Codifies the ten reusable patterns that emerged across the matrix:
mutation-free Detect/Apply split, Load/LoadForRuntime + cfg!=nil
contract, unexported xxxFromNewDir testable seam, malformed-old fails
loud before CopyNeeded, companion-plaintext-file dual-probe with
parsed-projection equality, 7-var statedirtest.Hermetic, the cleanup-
command recovery contract (cleanup must not be blocked by the broken
state it exists to wipe), init-gate ordering proof, reflect.DeepEqual
on default-applied Config, path-identity dedup.

Reaffirms §5b tier-2 deferral: only jtk needs the registry/DAG/fetcher
shape today (gro uses tier-1 only; cfl has no cache). Promoting from
one consumer would just relocate jtk code. Re-evaluate when cfl gains
a cache.

Closes #19

* fixup: Codex r1 factual corrections

- Atlassian PR numbers: #371→#373 (MON-5369 jtk-cache), #372→#374
  (MON-5370 shared config). SHAs were already correct.
- Pattern 5 (companion-secret-file dual-probe): format-dependent
  equality, not a single rule — text key=value uses parsed projection
  (nrq); opaque blobs use trimmed raw serialized value (gro's
  token.json). Generic shape described as old/new candidate
  enumeration + path-identity dedup, not nrq's specific helper trio.
- Pattern 9 (material equality): use whole-struct DeepEqual on
  default-applied Config ONLY when every field is directly comparable
  and user-meaningful (slck/nrq); build an explicit projection when
  fields need semantic normalization (gro: OAuthClientPath default
  equivalence, sorted GrantedScopes).
- §5b tier-2 reaffirmation: "second tier-2-shape consumer," not
  "third" (the criteria are ≥2 consumers).

* fixup: Codex r2 — pattern 5 conditional cleanup-helper rule; pattern 9 typo

* fixup: Codex r3 nit — narrow gro cleanup-scope wording
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant