{
  "sourceKind": "separate production-editor working copy; not a public GitHub commit",
  "reviewedAt": "2026-10-22",
  "excerpts": [
    {
      "file": "app/studio/layer-tree.ts",
      "start": 4,
      "end": 6,
      "code": "export function ancestors(doc:StudioDoc,groupId?:string){const result:LayerGroup[]=[],seen=new Set<string>();while(groupId){if(seen.has(groupId))throw Error('A group cannot contain itself.');seen.add(groupId);const g=doc.groups?.find(v=>v.id===groupId);if(!g)break;result.push(g);groupId=g.parentId;}return result;}\nexport function layerVisible(doc:StudioDoc,l:Layer){return l.visible&&ancestors(doc,l.groupId).every(g=>g.visible);}\nexport function layerLocked(doc:StudioDoc,l:Layer,kind:'all'|'position'|'pixels'='all'){return l.locked||ancestors(doc,l.groupId).some(g=>g.locked)||(kind==='position'?!!l.lockPosition:kind==='pixels'?!!l.lockPixels:false);}",
      "ref": "working-copy-2026-10-22",
      "sha256": "f5427be278b1aa065b419e3ff7368a7460092a420f00640269ec82b20471f8dd"
    },
    {
      "file": "app/studio/layer-style-commands.ts",
      "start": 3,
      "end": 9,
      "code": "import {validateEffects,type LayerEffects} from './layer-effects.ts';\n\nexport type StyleSnapshot={effects:LayerEffects;nativeEffects?:string;original?:string};\nexport function hasLayerStyle(layer?:Layer){return !!layer&&(Object.keys(layer.effects??{}).length>0||!!layer.psdData?.effects);}\nexport function validateStyleSnapshot(value:StyleSnapshot){\n if(!value||typeof value!=='object')throw Error('Missing copied layer style.');\n validateEffects(value.effects);",
      "ref": "working-copy-2026-10-22",
      "sha256": "8175351406d76893f74339b9e762760b6811c8894c1b7026f2bd6515a942b2ac"
    },
    {
      "file": "app/studio/blend-if.ts",
      "start": 8,
      "end": 10,
      "code": "export function validateBlendIf(b:BlendIf){if(!b||typeof b.enabled!=='boolean'||!['gray','red','green','blue','cyan','magenta','yellow','black','lightness','a','b'].includes(b.channel))throw Error('Invalid Blend If settings.');for(const a of [b.source,b.underlying])if(!Array.isArray(a)||a.length!==4||a.some((v,i)=>!Number.isFinite(v)||v<0||v>255||(i>0&&v<a[i-1])))throw Error('Invalid Blend If range.');if(b.additional){if(!Array.isArray(b.additional)||b.additional.length>4||new Set([b.channel,...b.additional.map(r=>r.channel)]).size!==b.additional.length+1)throw Error('Invalid additional Blend If channels.');for(const range of b.additional)validateBlendIf({...range,enabled:true,additional:undefined});}}\n// The two halves define a continuous ramp; an unsplit endpoint remains fully included.\nexport function tonalWeight(v:number,r:TonalRange){const [a,b,c,d]=r;return (v<a?0:v<b?(v-a)/(b-a):1)*(v>d?0:v>c?(d-v)/(d-c):1);}",
      "ref": "working-copy-2026-10-22",
      "sha256": "a949acdee8cfde43325c8c8dc7d196faa55f15708ab2bf5aea60035ad3cc798b"
    },
    {
      "file": "app/studio/layer-comps.ts",
      "start": 6,
      "end": 16,
      "code": "export type CompLayer = Pick<Layer,'id'> & Partial<Pick<Layer,'visible'|'x'|'y'|'width'|'height'|'rotation'|'transformQuad'|'flipX'|'flipY'|'opacity'|'fill'|'blend'|'effects'>>;\nexport type CompGroup = Pick<LayerGroup,'id'> & Partial<Pick<LayerGroup,'visible'|'opacity'|'fill'|'blend'|'effects'>>;\nexport type LayerComp = {id:string;name:string;comment?:string;capturedInfo?:number;nativeId?:number;groups?:CompGroup[];layers:CompLayer[]};\nexport const VISIBILITY=1,POSITION=2,APPEARANCE=4;\nconst positionKeys=['x','y','width','height','rotation','transformQuad','flipX','flipY'] as const;\nconst appearanceKeys=['opacity','fill','blend','effects'] as const;\nexport function captureComp(doc:StudioDoc,name:string,flags=7,comment='',previous?:LayerComp):LayerComp {\n if(!Number.isInteger(flags)||flags<1||flags>7)throw Error('Choose at least one property to capture.');\n return {id:previous?.id??uid(),nativeId:previous?.nativeId,name:name.trim()||'Layer Comp',comment,capturedInfo:flags,\n  layers:doc.layers.map(l=>{const s:CompLayer={id:l.id};if(flags&VISIBILITY)s.visible=l.visible;if(flags&POSITION)for(const k of positionKeys)(s as any)[k]=structuredClone(l[k]);if(flags&APPEARANCE)for(const k of appearanceKeys)(s as any)[k]=structuredClone(l[k]);return s;}),\n  groups:doc.groups?.map(g=>{const s:CompGroup={id:g.id};if(flags&VISIBILITY)s.visible=g.visible;if(flags&APPEARANCE)for(const k of appearanceKeys)(s as any)[k]=structuredClone(g[k]);return s;})};",
      "ref": "working-copy-2026-10-22",
      "sha256": "a973ae40b069df4d876f4e480b6d0b2c10068b5d64f1e9b9bac22021ceb2ce02"
    }
  ],
  "productionUI": {
    "date": "2026-10-22",
    "passed": true,
    "route": "Photoshot project save and reopen",
    "groupLockedChildTransformDisabled": true,
    "group": {
      "id": "accf1cbb-0a16-4083-94d8-2650b41a9d40",
      "name": "Portrait study",
      "visible": true,
      "locked": true,
      "collapsed": false,
      "opacity": 0.85,
      "blend": "normal"
    },
    "comps": [
      {
        "name": "White frame",
        "capturedInfo": 7
      },
      {
        "name": "Grouped preview",
        "capturedInfo": 7
      }
    ],
    "stroke": {
      "enabled": true,
      "color": "#ffffff",
      "color2": "#000000",
      "opacity": 100,
      "blend": "normal",
      "size": 24,
      "distance": 5,
      "angle": 120,
      "spread": 0,
      "position": "inside",
      "reverse": false,
      "scale": 100,
      "depth": 100,
      "altitude": 30,
      "direction": "up",
      "style": "inner"
    },
    "layerPropertiesPreserved": true,
    "blendIfFreshRun": false
  },
  "historicalReports": [
    {
      "file": "precision-blend-if-results.json",
      "sha256": "18d41621d5529b57c2c205bf090cfb4894fc8432a8693de235b66934fab1c44f",
      "results": {
        "passed": true,
        "checks": [
          "blend-if-rgb8.psd",
          "blend-if-rgb8.psb",
          "blend-if-rgb16.psd",
          "blend-if-rgb16.psb",
          "blend-if-rgb32.psd",
          "blend-if-rgb32.psb",
          "blend-if-cmyk8.psd",
          "blend-if-cmyk8.psb",
          "blend-if-cmyk16.psd",
          "blend-if-cmyk16.psb",
          "blend-if-lab16.psd",
          "blend-if-lab16.psb",
          "blend-if-gray8.psd",
          "blend-if-gray8.psb",
          "blend-if-gray16.psd",
          "blend-if-gray16.psb",
          "blend-if-gray32.psd",
          "blend-if-gray32.psb"
        ]
      },
      "freshRun": false
    },
    {
      "file": "precision-groups-adjustments-results.json",
      "sha256": "1d7de20fd0a279edc4af6a8367a18cc81b2910aef9f2be89aab53ce9b2d3b5f7",
      "results": {
        "passed": true,
        "checks": [
          {
            "kind": "rgb16",
            "large": false,
            "groupMask": "exact",
            "adjustment": "editable"
          },
          {
            "kind": "rgb16",
            "large": true,
            "groupMask": "exact",
            "adjustment": "editable"
          },
          {
            "kind": "rgb32",
            "large": false,
            "groupMask": "exact",
            "adjustment": "editable"
          },
          {
            "kind": "rgb32",
            "large": true,
            "groupMask": "exact",
            "adjustment": "editable"
          },
          {
            "kind": "rgb16",
            "nativePhotoshop": true,
            "groupMask": "exact",
            "adjustment": "editable"
          },
          {
            "kind": "rgb32",
            "nativePhotoshop": true,
            "groupMask": "exact",
            "adjustment": "editable"
          }
        ]
      },
      "freshRun": false
    }
  ]
}