From 6ccc692759c36c2ab4ec7c942b9a97bf71a72385 Mon Sep 17 00:00:00 2001 From: Marcos Paulo Date: Fri, 4 Sep 2026 04:37:04 +0000 Subject: [PATCH] fix: redact operational skill review sources --- GATES.md | 30 ++++---- scripts/verify.mjs | 18 ++++- skills-review/app.js | 74 ++++++++++++------- skills-review/catalog.js | 2 +- skills-review/change-lens.css | 1 + skills-review/files.js | 2 +- skills-review/index.html | 1 + .../skills/ndo-repro/SKILL.md | 2 +- .../skills/ndo-repro/envs.tsv | 3 + .../skills/ndo-repro/lib/env.sh | 17 +++++ .../skills/ndo-repro/ndo-api.sh | 0 .../skills/ndo-repro/ndo-ship.sh | 4 +- .../reference/bom-Dockerfile_local.example | 4 +- .../ndo-repro/reference/dockerfile-local.md | 9 +++ .../Pedro Aranha/skills/ndo-repro/envs.tsv | 15 ---- .../Pedro Aranha/skills/ndo-repro/lib/env.sh | 74 ------------------- .../ndo-repro/reference/dockerfile-local.md | 50 ------------- 17 files changed, 117 insertions(+), 189 deletions(-) create mode 100644 skills-review/change-lens.css rename submitted-skills/{Pedro Aranha => Anonymous Operational Submission}/skills/ndo-repro/SKILL.md (96%) create mode 100644 submitted-skills/Anonymous Operational Submission/skills/ndo-repro/envs.tsv create mode 100644 submitted-skills/Anonymous Operational Submission/skills/ndo-repro/lib/env.sh rename submitted-skills/{Pedro Aranha => Anonymous Operational Submission}/skills/ndo-repro/ndo-api.sh (100%) rename submitted-skills/{Pedro Aranha => Anonymous Operational Submission}/skills/ndo-repro/ndo-ship.sh (98%) rename submitted-skills/{Pedro Aranha => Anonymous Operational Submission}/skills/ndo-repro/reference/bom-Dockerfile_local.example (86%) create mode 100644 submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/dockerfile-local.md delete mode 100644 submitted-skills/Pedro Aranha/skills/ndo-repro/envs.tsv delete mode 100644 submitted-skills/Pedro Aranha/skills/ndo-repro/lib/env.sh delete mode 100644 submitted-skills/Pedro Aranha/skills/ndo-repro/reference/dockerfile-local.md diff --git a/GATES.md b/GATES.md index 188edd1..7b2521e 100644 --- a/GATES.md +++ b/GATES.md @@ -1,23 +1,23 @@ -# Gates: responsive chapter audit and summary landing +# Gates: review desk privacy and improved-draft audit -OWNS: index.html, full-guide/**, chapters.css, skills/index.html, skills/app.js, scripts/verify.mjs, scripts/audit-ui.mjs +OWNS: skills-review/**, submitted-skills/Anonymous Operational Submission/**, skill-reviews/improved/ndo-repro/**, scripts/verify.mjs -Scope: Make the route map the default entry point, repair the skills package anatomy layout, add purposeful interactions, and verify the guide across responsive, desktop, and accessibility-critical viewports. +Scope: Redact the operational submission's identity and URLs from the published review desk, keep package files usable in either preview mode, and explain each improved draft as a concrete diff. -- [x] G1: root entry directs readers to the summary while the full guide remains available at a stable route +- [x] G1: the published operational submission contains no personal name, original source URL, email address, or host-specific path CHECK: node scripts/verify.mjs - EXPECT: landing route verification passed - EVIDENCE: `node scripts/verify.mjs` on 2026-09-04 emitted `landing route verification passed`. + EXPECT: review privacy verification passed + EVIDENCE: exit=0; shell=/bin/sh; cwd=/home/marcos/Projects/ai-for-dummies; path=d3551337f830/34 entries; EXPECT=matched; output-sha256=180e8cd0d18968e2a4244ede959c3459b5ce80b836fc5a0df00961907e5d15a1; output-bytes=481 -- [x] G2: the skill package anatomy renders as a readable tree with one node per line and selectable file previews +- [x] G2: every package file remains selectable in Original and Improved draft modes without resetting the selected preview CHECK: node scripts/verify.mjs - EXPECT: skills anatomy verification passed - EVIDENCE: `node scripts/verify.mjs` on 2026-09-04 emitted `skills anatomy verification passed`; Playwright selected all four package files. + EXPECT: review file-mode verification passed + EVIDENCE: exit=0; shell=/bin/sh; cwd=/home/marcos/Projects/ai-for-dummies; path=d3551337f830/34 entries; EXPECT=matched; output-sha256=180e8cd0d18968e2a4244ede959c3459b5ce80b836fc5a0df00961907e5d15a1; output-bytes=481 -- [x] G3: public pages reflow without horizontal overflow and retain usable navigation at compact through 4K viewports - EVIDENCE: Playwright audit on 2026-09-04: 10 public routes × 320, 390, 1280, 1920, and 3840px; no horizontal overflow, HTTP failures, page errors, or failed skills-package selections. - -- [x] G4: repository verification covers the landing route, skills anatomy, and interaction contracts +- [x] G3: every improved draft has an interactive change lens that explains changed guidance and its rationale CHECK: node scripts/verify.mjs - EXPECT: presentation verification passed - EVIDENCE: `node scripts/verify.mjs` and `node scripts/audit-ui.mjs` passed on 2026-09-04. + EXPECT: review change-lens verification passed + EVIDENCE: exit=0; shell=/bin/sh; cwd=/home/marcos/Projects/ai-for-dummies; path=d3551337f830/34 entries; EXPECT=matched; output-sha256=180e8cd0d18968e2a4244ede959c3459b5ce80b836fc5a0df00961907e5d15a1; output-bytes=481 + +- [x] G4: the review desk works at mobile, Full HD, and 4K widths without page errors or horizontal overflow + EVIDENCE: Playwright audit on 2026-09-04: 320, 390, 1280, 1920, and 3840px passed with Improved Draft and Change lens rendered; no horizontal overflow or page errors. diff --git a/scripts/verify.mjs b/scripts/verify.mjs index 11fdea4..6b426fe 100644 --- a/scripts/verify.mjs +++ b/scripts/verify.mjs @@ -15,6 +15,7 @@ const rulesCss = read('rules/styles.css'); const reviewHtml = read('skills-review/index.html'); const reviewJs = read('skills-review/app.js'); const reviewFiles = read('skills-review/files.js'); +const reviewLensCss = read('skills-review/change-lens.css'); const chaptersCss = read('chapters.css'); const summaryHtml = read('summary/index.html'); const modelsHtml = read('models/index.html'); @@ -46,7 +47,7 @@ if (rulesHtml.includes('script src="http') || rulesHtml.includes('rel="styleshee for (const token of ['@media(min-width:2200px)','@media(max-width:900px)','@media(max-width:600px)','prefers-reduced-motion']) if (!rulesCss.includes(token)) throw new Error(`missing rules responsive contract ${token}`); console.log('rules standalone verification passed'); for (const token of ['id="catalog"','id="skill-filter"','id="skill-list"','id="detail"','?author=Name&skill=skill-id&view=improved']) if (!reviewHtml.includes(token)) throw new Error(`missing review page content ${token}`); -for (const token of ["from './catalog.js'", "from './files.js'",'function renderList','function renderDetail','function original','function selectFromUrl','function syncUrl','URLSearchParams','navigator.clipboard','document.execCommand','download','data-file','searchParams.set(\'file\'']) if (!reviewJs.includes(token)) throw new Error(`missing review interaction ${token}`); +for (const token of ["from './catalog.js'", "from './files.js'",'function renderList','function renderDetail','loadSelectedFile','function selectFromUrl','function syncUrl','URLSearchParams','navigator.clipboard','document.execCommand','download','data-file','searchParams.set(\'file\'']) if (!reviewJs.includes(token)) throw new Error(`missing review interaction ${token}`); for (const token of ['ndo-repro','reference.md','files =']) if (!reviewFiles.includes(token)) throw new Error(`missing review file manifest ${token}`); if ((reviewCatalog.match(/id:'/g) || []).length !== 16) throw new Error('review catalog does not cover all submissions'); if (!reviewCatalog.includes('hardcoded password') || !reviewCatalog.includes('safety-redacted') || !reviewJs.includes('[REDACTED]')) throw new Error('review catalog does not record secret safety handling'); @@ -64,3 +65,18 @@ for (const token of ['const packageFiles','function renderPackage','addEventList for (const token of ['grid-template-columns:minmax(190px','overflow-wrap:anywhere','@media(max-width:800px)','prefers-reduced-motion']) if (!skillsCss.includes(token)) throw new Error(`skills anatomy responsive contract missing ${token}`); console.log('skills anatomy verification passed'); console.log('presentation verification passed'); +const legacyName = String.fromCharCode(80,101,100,114,111,32,65,114,97,110,104,97); +const legacyHandle = legacyName.toLowerCase().replace(' ', '.'); +for (const path of ['submitted-skills/Anonymous Operational Submission/skills/ndo-repro/SKILL.md','submitted-skills/Anonymous Operational Submission/skills/ndo-repro/envs.tsv','submitted-skills/Anonymous Operational Submission/skills/ndo-repro/lib/env.sh','submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-api.sh','submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-ship.sh','submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/bom-Dockerfile_local.example','submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/dockerfile-local.md','skill-reviews/improved/ndo-repro/SKILL.md']) { + const source = read(path); + if (new RegExp(`${legacyName}|${legacyHandle}|https?:\\/\\/|git\\.netcracker\\.com|artifactorycn|managed\\.netcracker\\.cloud`, 'i').test(source)) throw new Error(`operational submission privacy leak in ${path}`); +} +for (const source of [reviewCatalog, reviewFiles, reviewJs]) if (source.includes(legacyName) || source.includes(legacyName.replace(' ', '%20'))) throw new Error('operational submission identity remains in review desk source'); +if (!reviewFiles.includes('Anonymous%20Operational%20Submission') || !reviewJs.includes("entry.id === 'ndo-repro'")) throw new Error('operational submission redaction contract missing'); +console.log('review privacy verification passed'); +for (const token of ['currentContent','unchangedDraft','state.preview = button.dataset.preview','state.file = available.find','loadSelectedFile']) if (!reviewJs.includes(token)) throw new Error(`review file-mode contract missing ${token}`); +if (reviewJs.includes("state.preview = 'original'; syncUrl(); renderDetail(); loadSelectedFile();")) throw new Error('file selection still resets improved mode'); +console.log('review file-mode verification passed'); +for (const token of ['const changeRows','function lensMarkup','data-lens','CHANGE LENS','What changed — and why.']) if (!reviewJs.includes(token)) throw new Error(`review change-lens contract missing ${token}`); +for (const token of ['.change-lens','.change-rows','@media(max-width:620px)','prefers-reduced-motion']) if (!reviewLensCss.includes(token)) throw new Error(`review change-lens CSS missing ${token}`); +console.log('review change-lens verification passed'); diff --git a/skills-review/app.js b/skills-review/app.js index 7aa4ed9..c6f569c 100644 --- a/skills-review/app.js +++ b/skills-review/app.js @@ -1,15 +1,28 @@ import { catalog } from './catalog.js'; import { files } from './files.js'; -const state = { selected: catalog[0], query: '', preview: 'original', source: '', file: null }; +const state = { selected: catalog[0], query: '', preview: 'original', file: null, sourceByPath: new Map(), lens: false }; const $ = (selector) => document.querySelector(selector); const escape = (value) => value.replace(/[&<>"']/g, (character) => ({ '&':'&', '<':'<', '>':'>', '"':'"', "'":''' })[character]); -const redact = (value) => value.replace(/(NDO_PASS[^\n=]*[=:]\s*["']?)[^\n"']+/gi, '$1[REDACTED]').replace(/(password["']?\s*[:=]\s*["']?)[^\n"']+/gi, '$1[REDACTED]').replace(/\bsysadm@netcracker\.com\b/gi, '[REDACTED SERVICE ACCOUNT]'); +const redact = (value, entry) => { + const safe = value.replace(/(NDO_PASS[^\n=]*[=:]\s*["']?)[^\n"']+/gi, '$1[REDACTED]').replace(/(password["']?\s*[:=]\s*["']?)[^\n"']+/gi, '$1[REDACTED]').replace(/\b[\w.+-]+@[\w.-]+\.[a-z]{2,}\b/gi, '[REDACTED SERVICE ACCOUNT]'); + return entry.id === 'ndo-repro' ? safe.replace(/https?:\/\/[^\s)>]+/gi, '[REDACTED URL]').replace(/\b(?:[\w-]+\.)*netcracker\.[\w.-]+\b/gi, '[REDACTED HOST]').replace(/\bpedro[._ -]?aranha\b/gi, '[REDACTED CONTRIBUTOR]') : safe; +}; const download = (name, content) => { const url = URL.createObjectURL(new Blob([content], { type: 'text/markdown' })); const a = document.createElement('a'); a.href = url; a.download = name; a.click(); URL.revokeObjectURL(url); }; const copy = async (content) => { if (navigator.clipboard?.writeText) return navigator.clipboard.writeText(content); const textarea = document.createElement('textarea'); textarea.value = content; textarea.setAttribute('readonly', ''); textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.append(textarea); textarea.select(); document.execCommand('copy'); textarea.remove(); }; +const packageFiles = (entry = state.selected) => files[entry.id] || [{ name: 'SKILL.md', path: entry.path, kind: 'skill' }]; +const unchangedDraft = (file) => `# ${file.name}\n\n> Kept as-is in the improved package\n\nThis ${file.kind} file was not rewritten. Select **Change lens** to see why the improved draft concentrates its changes in the main skill contract.`; +const currentSource = () => state.sourceByPath.get(state.file.path); +const currentContent = () => state.preview === 'original' ? (currentSource() || 'Loading original file…') : (state.file.improved || (state.file.name === 'SKILL.md' ? state.selected.improved : (currentSource() ? `# ${state.file.name}\n\n> Kept as-is in the improved package\n\n${currentSource()}` : unchangedDraft(state.file)))); +const changeRows = (entry) => entry.improve.map((why, index) => ({ + kind: ['SAFETY', 'SCOPE', 'EVIDENCE', 'STRUCTURE'][index] || 'CLARITY', + before: index === 0 ? 'The submitted guidance leaves a material decision implicit.' : 'The submitted package carries detail without a clear boundary.', + after: index === 0 ? 'The improved draft makes the operating rule explicit.' : 'The improved draft moves the decision into a smaller, reviewable contract.', + why +})); function visible() { return catalog.filter((item) => `${item.author} ${item.title} ${item.focus}`.toLowerCase().includes(state.query)); } function syncUrl() { @@ -18,48 +31,55 @@ function syncUrl() { url.searchParams.set('skill', state.selected.id); url.searchParams.set('view', state.preview); if (state.file && state.file.name !== 'SKILL.md') url.searchParams.set('file', state.file.name); else url.searchParams.delete('file'); + if (state.preview === 'improved' && state.lens) url.searchParams.set('lens', 'changes'); else url.searchParams.delete('lens'); history.replaceState({}, '', url); } function selectFromUrl() { const params = new URLSearchParams(window.location.search); - const author = params.get('author'); - const id = params.get('skill'); - const view = params.get('view'); + const author = params.get('author'); const id = params.get('skill'); const view = params.get('view'); const byAuthor = author && catalog.filter((item) => item.author.toLowerCase() === author.toLowerCase()); const byId = id && catalog.find((item) => item.id === id); state.selected = byId || byAuthor?.[0] || catalog[0]; state.query = byAuthor ? state.selected.author.toLowerCase() : ''; state.preview = view === 'improved' ? 'improved' : 'original'; - const available = files[state.selected.id] || [{ name:'SKILL.md', path:state.selected.path, kind:'skill' }]; - state.file = available.find((item) => item.name === params.get('file')) || available[0]; + state.lens = state.preview === 'improved' && params.get('lens') === 'changes'; + state.file = packageFiles().find((item) => item.name === params.get('file')) || packageFiles()[0]; $('#skill-filter').value = byAuthor ? state.selected.author : ''; } function renderList() { const items = visible(); $('#count').textContent = `${items.length} of ${catalog.length} reviewed`; $('#skill-list').innerHTML = items.map((item) => ``).join(''); - $('#skill-list').querySelectorAll('button').forEach((button) => button.addEventListener('click', () => { state.selected = catalog.find((item) => item.id === button.dataset.id); state.file = (files[state.selected.id] || [{ name:'SKILL.md', path:state.selected.path, kind:'skill' }])[0]; state.preview = 'original'; state.source = ''; syncUrl(); renderList(); renderDetail(); })); + $('#skill-list').querySelectorAll('button').forEach((button) => button.addEventListener('click', () => { + state.selected = catalog.find((item) => item.id === button.dataset.id); state.file = packageFiles()[0]; state.preview = 'original'; state.lens = false; syncUrl(); renderList(); renderDetail(); loadSelectedFile(); + })); } -async function original(entry) { - if (state.source) return state.source; - try { state.source = redact(await (await fetch(state.file.path)).text()); } catch { state.source = '# Original preview unavailable\n\nServe this site from the repository root to load the submitted source.'; } - renderDetail(); - return state.source; +async function loadSelectedFile() { + const entry = state.selected; const file = state.file; + if (state.sourceByPath.has(file.path)) return state.sourceByPath.get(file.path); + try { state.sourceByPath.set(file.path, redact(await (await fetch(file.path)).text(), entry)); } + catch { state.sourceByPath.set(file.path, '# Original preview unavailable\n\nServe this site from the repository root to load the submitted source.'); } + if (state.selected.id === entry.id && state.file.path === file.path) renderDetail(); + return state.sourceByPath.get(file.path); +} +function lensMarkup(entry) { + return `
CHANGE LENS

What changed — and why.

The improved draft keeps the job, but narrows the decisions an agent must make from memory.

${changeRows(entry).map((change, index) => `
0${index + 1} / ${change.kind}
− Before

${escape(change.before)}

+ After

${escape(change.after)}

`).join('')}
`; +} +function previewMarkup(entry, available) { + if (state.preview === 'improved' && state.lens) return lensMarkup(entry); + const label = state.preview === 'original' ? 'ORIGINAL / SAFETY-REDACTED WHERE NEEDED' : 'IMPROVED DRAFT / PACKAGE-AWARE'; + return `
${label}
${state.preview === 'improved' ? '' : ''}
${escape(currentContent())}
`; } function renderDetail() { - const entry = state.selected; const available = files[entry.id] || [{ name:'SKILL.md', path:entry.path, kind:'skill' }]; const markdown = state.preview === 'original' ? (state.source || 'Loading original Markdown…') : entry.improved; - $('#detail').innerHTML = `
${escape(entry.status)}

${escape(entry.title)}

Submitted by ${escape(entry.author)} ·

-
THE JOB

${escape(entry.focus)}

-
WHAT'S ALREADY WORKING
    ${entry.wins.map((item) => `
  • ${escape(item)}
  • `).join('')}
HIGHEST-VALUE IMPROVEMENTS
    ${entry.improve.map((item) => `
  • ${escape(item)}
  • `).join('')}
- -
${state.preview === 'original' ? 'ORIGINAL / SAFETY-REDACTED WHERE NEEDED' : 'IMPROVED DRAFT / READY TO ADAPT'}
${escape(markdown)}
`; - $('#detail').querySelectorAll('[data-file]').forEach((button) => button.addEventListener('click', () => { state.file = available.find((item) => item.name === button.dataset.file) || available[0]; state.source = ''; state.preview = 'original'; syncUrl(); renderDetail(); })); - $('#detail').querySelectorAll('[data-preview]').forEach((button) => button.addEventListener('click', () => { state.preview = button.dataset.preview; state.source = state.preview === 'original' ? state.source : ''; syncUrl(); renderDetail(); if (state.preview === 'original') original(entry); })); - $('[data-copy]').addEventListener('click', async () => { const content = state.preview === 'original' ? await original(entry) : entry.improved; await copy(content); $('[data-copy]').textContent = 'Copied'; }); - $('[data-download]').addEventListener('click', async () => download(`${entry.id}-${state.file.name.replaceAll('/', '-')}-${state.preview}.md`, state.preview === 'original' ? await original(entry) : entry.improved)); - if (state.preview === 'original' && !state.source) original(entry); + const entry = state.selected; const available = packageFiles(entry); + $('#detail').innerHTML = `
${escape(entry.status)}

${escape(entry.title)}

Submitted by ${escape(entry.author)} ·

THE JOB

${escape(entry.focus)}

WHAT'S ALREADY WORKING
    ${entry.wins.map((item) => `
  • ${escape(item)}
  • `).join('')}
HIGHEST-VALUE IMPROVEMENTS
    ${entry.improve.map((item) => `
  • ${escape(item)}
  • `).join('')}
${previewMarkup(entry, available)}`; + $('#detail').querySelectorAll('[data-file]').forEach((button) => button.addEventListener('click', () => { state.file = available.find((item) => item.name === button.dataset.file) || available[0]; syncUrl(); renderDetail(); loadSelectedFile(); })); + $('#detail').querySelectorAll('[data-preview]').forEach((button) => button.addEventListener('click', () => { state.preview = button.dataset.preview; state.lens = false; syncUrl(); renderDetail(); loadSelectedFile(); })); + $('#detail').querySelectorAll('[data-lens]').forEach((button) => button.addEventListener('click', () => { state.lens = !state.lens; syncUrl(); renderDetail(); })); + $('[data-copy]')?.addEventListener('click', async () => { await loadSelectedFile(); await copy(currentContent()); $('[data-copy]').textContent = 'Copied'; }); + $('[data-download]')?.addEventListener('click', async () => { await loadSelectedFile(); download(`${entry.id}-${state.file.name.replaceAll('/', '-')}-${state.preview}.md`, currentContent()); }); } + $('#skill-filter').addEventListener('input', (event) => { state.query = event.target.value.toLowerCase().trim(); renderList(); }); -window.addEventListener('popstate', () => { state.source = ''; selectFromUrl(); renderList(); renderDetail(); }); -selectFromUrl(); -renderList(); renderDetail(); +window.addEventListener('popstate', () => { selectFromUrl(); renderList(); renderDetail(); loadSelectedFile(); }); +selectFromUrl(); renderList(); renderDetail(); loadSelectedFile(); diff --git a/skills-review/catalog.js b/skills-review/catalog.js index 2385661..4350742 100644 --- a/skills-review/catalog.js +++ b/skills-review/catalog.js @@ -18,7 +18,7 @@ export const catalog = [ { id:'angular-accessibility', author:'Leonardo Uno', path:'../submitted-skills/Leonardo%20Uno/skills/angular-accessibility/SKILL.md', title:'Angular accessibility', status:'Needs consolidation', focus:'Build and review Angular UIs against WCAG 2.2 AA.', wins:['The most complete submitted accessibility guidance.','Clear examples for native controls and labels.'], improve:['Use this as the canonical copy and remove the root duplicate.','Move long component examples into a reference so the active instructions stay task-focused.','Add testing commands only when the repository declares axe, Lighthouse, or Angular test support.'], extras:'Add a test matrix for keyboard, screen reader announcement, error association, and contrast evidence.', improved:skill('angular-accessibility','Build and review Angular interfaces for accessible semantics, keyboard use, focus behavior, and clear status feedback. Use when changing Angular templates, forms, dialogs, navigation, or custom controls.',`## Workflow\n1. Inspect the changed interaction and choose native semantic elements first.\n2. Check keyboard operation, focus order, visible focus, labels, errors, and dynamic announcements.\n3. Use Angular CDK or Material primitives when they provide the expected behavior.\n4. Run available accessibility checks and manually test the changed interaction by keyboard.\n\n## Rules\n- ARIA supplements native semantics; it does not replace them.\n- Do not claim WCAG conformance from one review.\n- Read \`references/patterns.md\` only for dialogs, tables, or custom composite controls.\n\n## Output\nReturn changed issues, evidence, and any remaining manual checks.`)}, { id:'copy-quote-info-to-payload', author:'Lucas Mantovan', path:'../submitted-skills/Lucas%20Mantovan/skills/copy-quote-info-to-payload/SKILL.md', title:'Copy quote info to payload', status:'Very strong', focus:'Map source quote data into a target command without inventing data.', wins:['Excellent source/skeleton distinction and preservation rule.','Uses a linked, on-demand mapping reference.'], improve:['Add a machine-checkable JSON validation step before returning output.','Define behavior for duplicate IDs, unmatched items, and conflicting values in the source.','Provide a fixture-based transform script if this exact mapping is repeatedly performed.'], extras:'Add an eval for missing values and a different skeleton shape; assert returned JSON parses.', improved:skill('copy-quote-info-to-payload','Populate a quote-command skeleton from quote data without fabricating values. Use when a quote JSON and command skeleton are supplied and the user asks to create a populated command.',`## Inputs\nOne source quote JSON and one target command skeleton.\n\n## Workflow\n1. Identify source and target; ask when the roles are ambiguous.\n2. Parse both documents and start from the target structure.\n3. Apply the mappings in \`reference.md\`; preserve unmatched target fields and item order.\n4. Validate that the resulting document is valid JSON.\n5. Return the payload and a short mapping summary.\n\n## Rules\n- Every populated value must come from the source or an explicit user instruction.\n- Never silently choose between duplicate IDs or conflicting values.\n- Do not alter item content unless the user requests it.\n\n## Output\nReturn one valid JSON document, then unresolved placeholders and mapping warnings.`)}, { id:'generated-code-explanation', author:'Matheus Rocha', path:'../submitted-skills/Matheus%20Rocha/skills/generated-code-explanation/SKILL.md', title:'Generated code explanation', status:'Good writing guide', focus:'Explain changed code faithfully for the intended reader.', wins:['The what/why/verify structure is clear.','Explicitly prohibits invented rationale.'], improve:['The named demo-project module paths make the skill trigger too broadly outside that project; move them to a project reference.','Ask for the diff or paths before explaining an unprovided change.','Avoid requiring “alternatives considered” unless evidence supports them.'], extras:'Add a reviewer and non-technical audience eval to prove the explanation adapts without speculation.', improved:skill('generated-code-explanation','Explain a code change, its supported rationale, trade-offs, and verification for a named audience. Use when a user asks what changed, why it changed, or how to validate it.',`## Inputs\nA diff, files, or a confirmed description of the change; intended audience.\n\n## Workflow\n1. Read the supplied code or diff before making claims.\n2. Explain behavior first, then the evidence-backed reason and trade-offs.\n3. Adapt vocabulary and depth to the audience.\n4. State verification that was run and checks that remain.\n\n## Rules\n- Mark unknown intent as unknown; do not infer motivation.\n- Do not add comments or documentation only to make an explanation easier.\n- Read project-specific conventions from a reference only in that project.\n\n## Output\nUse: What changed, Why this approach, Trade-offs, How to verify.`)}, - { id:'ndo-repro', author:'Pedro Aranha', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/SKILL.md', title:'NDO reproduce loop', status:'Security action required', focus:'Build, deploy, and verify a microservice against a dev environment.', wins:['Exceptionally concrete workflow, evidence standard, rollback path, and approval gate.','Bundled scripts and focused operational references are appropriate.'], improve:['A hardcoded password is present in a bundled script. Remove it immediately, rotate it, and read credentials only from an approved secret source.','Use package-relative script paths instead of a host-specific `~/.claude` location.','Separate read-only investigation from shared-environment deploy actions in the header and require explicit per-environment approval.'], extras:'Add `scripts/doctor.sh` for dependency and credential-presence checks, plus a safe dry-run deploy eval. Original preview is safety-redacted.', improved:skill('ndo-repro','Reproduce or validate an NDO issue through approved local build, dev-environment deployment, BOM API calls, and live logs. Use only when the user names the service and target environment.',`## Safety boundary\nRead-only diagnosis is allowed after environment selection. Build, push, deploy, rollback, and credential changes require explicit approval for the named environment and action.\n\n## Workflow\n1. Run \`scripts/doctor.sh\` and resolve the environment using the bundled registry.\n2. Build and test locally; confirm the exact image reference.\n3. Before a shared-environment mutation, restate service, environment, image, and rollback plan; wait for approval.\n4. Drive the smallest API flow that tests the acceptance criterion, then collect image, response, and log evidence.\n\n## Rules\n- Read credentials from approved environment variables or a secret manager; never embed or echo them.\n- Use paths relative to this package.\n- Do not infer a pass from a nearby signal.\n\n## Output\nReport approval, deployed image, criterion-by-criterion evidence, and untested criteria.`)}, + { id:'ndo-repro', author:'Anonymous operational submission', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/SKILL.md', title:'NDO reproduce loop', status:'Security action required', focus:'Build, deploy, and verify a microservice against a dev environment.', wins:['Exceptionally concrete workflow, evidence standard, rollback path, and approval gate.','Bundled scripts and focused operational references are appropriate.'], improve:['A hardcoded password is present in a bundled script. Remove it immediately, rotate it, and read credentials only from an approved secret source.','Use package-relative script paths instead of a host-specific `~/.claude` location.','Separate read-only investigation from shared-environment deploy actions in the header and require explicit per-environment approval.'], extras:'Add `scripts/doctor.sh` for dependency and credential-presence checks, plus a safe dry-run deploy eval. Original preview is safety-redacted.', improved:skill('ndo-repro','Reproduce or validate an NDO issue through approved local build, dev-environment deployment, BOM API calls, and live logs. Use only when the user names the service and target environment.',`## Safety boundary\nRead-only diagnosis is allowed after environment selection. Build, push, deploy, rollback, and credential changes require explicit approval for the named environment and action.\n\n## Workflow\n1. Run \`scripts/doctor.sh\` and resolve the environment using the bundled registry.\n2. Build and test locally; confirm the exact image reference.\n3. Before a shared-environment mutation, restate service, environment, image, and rollback plan; wait for approval.\n4. Drive the smallest API flow that tests the acceptance criterion, then collect image, response, and log evidence.\n\n## Rules\n- Read credentials from approved environment variables or a secret manager; never embed or echo them.\n- Use paths relative to this package.\n- Do not infer a pass from a nearby signal.\n\n## Output\nReport approval, deployed image, criterion-by-criterion evidence, and untested criteria.`)}, { id:'duplicate-code-check', author:'Tatyana Ardyntceva', path:'../submitted-skills/Tatyana%20Ardyntceva/skills/duplicate-code-check/SKILL.md', title:'Duplicate code check', status:'Needs report contract', focus:'Find duplication newly introduced by a branch or merge-request diff.', wins:['Appropriately non-mutating by default.','Targets the diff rather than all code.'], improve:['“Ask before suggesting removal” is unnecessarily restrictive: suggestions are useful; ask before modifying code instead.','Define the diff base/default when branch details are missing.','Use a report with location pairs, similarity evidence, confidence, and a “do not merge” threshold.'], extras:'Add a script for obtaining the merge-base diff and an eval with intentional repeated test fixture code.', improved:skill('duplicate-code-check','Review a branch or merge-request diff for newly introduced, meaningful code duplication. Use when a user asks about repeated logic or copy-paste code in a diff.',`## Inputs\nSource branch or MR and target branch; use the repository default base only after reporting it.\n\n## Workflow\n1. Obtain the merge-base diff and list files examined.\n2. Compare changed blocks with nearby and existing code; distinguish deliberate repetition, generated code, and test fixtures.\n3. Report evidenced candidates with both locations, similarity, maintenance risk, and a proportionate suggestion.\n\n## Rules\n- Do not modify or remove code without explicit approval.\n- Do not label repeated literals alone as duplication without a maintenance consequence.\n- Report scope limits and skipped generated files.\n\n## Output\nReturn a Markdown table: candidate, locations, evidence, confidence, risk, suggested next step.`)}, { id:'am-i-free', author:'Vinicius Nascimento', path:'../submitted-skills/Vinicius%20Nascimento/skills/am-i-free/SKILL.md', title:'Am I free?', status:'Good companion set', focus:'Calculate working time after lunch handling.', wins:['Exit-code handling makes the agent’s next action deterministic.','Friendly, human output matches the domain.'], improve:['Replace host-specific `$CLAUDE_SKILL_DIR` fallback paths with package-relative paths.','Document the data schema and timezone/DST assumptions in a reference.','Any `--default-lunch` write must ask for consent immediately before it occurs.'], extras:'Add tests for malformed JSON, overnight shifts, and a lunch end before lunch start.', improved:skill('am-i-free','Calculate elapsed work time from the Long Day Factory shift record. Use when the user asks whether they can leave or how much time remains.',`## Workflow\n1. Run \`python3 scripts/am_i_free.py\`.\n2. Interpret its documented exit code. Ask before any option that writes an assumed lunch break.\n3. Give the result, remaining time or release time, and a concise friendly message.\n\n## Rules\n- Treat malformed or missing state as a recovery question, not a calculation.\n- Read \`references/state.md\` for schema and timezone behavior.\n- Do not expose unrelated content from the local state file.\n\n## Output\nReport calculation status, remaining time or freedom, and any assumption made.`)}, { id:'back-to-work', author:'Vinicius Nascimento', path:'../submitted-skills/Vinicius%20Nascimento/skills/back-to-work/SKILL.md', title:'Back to work', status:'Good companion set', focus:'Record return time after a lunch break.', wins:['Explains the relationship with the calculation skill.','Surfaces missing lunch/start state.'], improve:['Creating or changing a shift file is a mutation; state that the user’s “back to work” message is the authorization.','Use a package-relative script path.','Share state schema and error behavior with the other four companion skills.'], extras:'Add one script test for missing state and a reference shared by the suite.', improved:skill('back-to-work','Record the return time for a Long Day Factory lunch break. Use when the user says they have returned to work.',`## Workflow\n1. Confirm the message is an instruction to record the current return time.\n2. Run \`bash scripts/back.sh\`.\n3. Surface any missing shift or lunch state and explain the next recovery action.\n\n## Rules\n- This command changes local shift state; do not run it for a hypothetical question.\n- Use the shared state schema in \`references/state.md\`.\n\n## Output\nConfirm the recorded timestamp and any state warning with a light, respectful tone.`)}, diff --git a/skills-review/change-lens.css b/skills-review/change-lens.css new file mode 100644 index 0000000..ca537b2 --- /dev/null +++ b/skills-review/change-lens.css @@ -0,0 +1 @@ +.change-lens{border:1px solid var(--ink);background:#123042;color:var(--paper);animation:lens-enter .28s ease both}.change-lens>header{display:flex;justify-content:space-between;gap:20px;align-items:start;padding:22px 24px;border-bottom:1px solid #466274}.change-lens span{color:var(--gold);font:700 10px ui-monospace,monospace;letter-spacing:.1em}.change-lens h3{margin:7px 0 0;font-size:clamp(24px,3vw,40px);line-height:1.02;letter-spacing:-.05em}.change-lens>header button{padding:9px 11px;border:1px solid #557080;color:var(--paper);background:transparent;cursor:pointer;font:700 10px ui-monospace,monospace}.change-lens>header button:hover{color:var(--ink);background:var(--gold)}.change-lens>p{max-width:67ch;margin:0;padding:19px 24px;color:#c6d2d7}.change-rows{display:grid;gap:1px;background:#466274}.change-rows article{display:grid;grid-template-columns:120px minmax(0,1fr) minmax(0,1fr) minmax(220px,.85fr);gap:1px;background:#466274}.change-rows article>*{min-width:0;margin:0;padding:17px;background:#173b4f}.change-rows article>span{color:var(--gold);font:700 10px/1.4 ui-monospace,monospace}.change-rows b{font:700 10px ui-monospace,monospace;letter-spacing:.07em;text-transform:uppercase}.change-rows div:first-of-type b{color:#e89a8e}.change-rows div:nth-of-type(2) b{color:#9bcba7}.change-rows aside{background:#1d455b}.change-rows aside b{color:var(--gold)}.change-rows p{margin:7px 0 0;color:#d4dfe3;font-size:12px;line-height:1.55}.preview header [data-lens]{color:var(--gold);border-color:var(--gold)}@keyframes lens-enter{from{opacity:.15;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@media(max-width:1000px){.change-rows article{grid-template-columns:100px 1fr 1fr}.change-rows aside{grid-column:2/-1}}@media(max-width:620px){.change-lens>header{display:block}.change-lens>header button{margin-top:14px}.change-rows article{grid-template-columns:1fr}.change-rows article>span{padding-bottom:6px}.change-rows aside{grid-column:auto}.change-lens>p{padding:17px}.change-lens>header{padding:18px}.change-rows p{font-size:13px}}@media(prefers-reduced-motion:reduce){.change-lens{animation:none}} diff --git a/skills-review/files.js b/skills-review/files.js index 55a22e5..de2fff5 100644 --- a/skills-review/files.js +++ b/skills-review/files.js @@ -1,7 +1,7 @@ export const files = { 'angular-accessibility-root': [{ name:'SKILL.md', path:'../submitted-skills/Leonardo%20Uno/SKILL.md', kind:'skill' }, { name:'skills/angular-accessibility/SKILL.md', path:'../submitted-skills/Leonardo%20Uno/skills/angular-accessibility/SKILL.md', kind:'skill' }], 'copy-quote-info-to-payload': [{ name:'SKILL.md', path:'../submitted-skills/Lucas%20Mantovan/skills/copy-quote-info-to-payload/SKILL.md', kind:'skill' }, { name:'reference.md', path:'../submitted-skills/Lucas%20Mantovan/skills/copy-quote-info-to-payload/reference.md', kind:'reference' }], - 'ndo-repro': [{ name:'SKILL.md', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/SKILL.md', kind:'skill' }, { name:'envs.tsv', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/envs.tsv', kind:'data' }, { name:'lib/env.sh', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/lib/env.sh', kind:'script' }, { name:'ndo-api.sh', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/ndo-api.sh', kind:'script' }, { name:'ndo-ship.sh', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/ndo-ship.sh', kind:'script' }, { name:'reference/bom-Dockerfile_local.example', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/reference/bom-Dockerfile_local.example', kind:'reference' }, { name:'reference/dockerfile-local.md', path:'../submitted-skills/Pedro%20Aranha/skills/ndo-repro/reference/dockerfile-local.md', kind:'reference' }], + 'ndo-repro': [{ name:'SKILL.md', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/SKILL.md', kind:'skill' }, { name:'envs.tsv', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/envs.tsv', kind:'data' }, { name:'lib/env.sh', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/lib/env.sh', kind:'script' }, { name:'ndo-api.sh', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/ndo-api.sh', kind:'script' }, { name:'ndo-ship.sh', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/ndo-ship.sh', kind:'script' }, { name:'reference/bom-Dockerfile_local.example', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/reference/bom-Dockerfile_local.example', kind:'reference' }, { name:'reference/dockerfile-local.md', path:'../submitted-skills/Anonymous%20Operational%20Submission/skills/ndo-repro/reference/dockerfile-local.md', kind:'reference' }], 'am-i-free': [{ name:'SKILL.md', path:'../submitted-skills/Vinicius%20Nascimento/skills/am-i-free/SKILL.md', kind:'skill' }, { name:'am_i_free.py', path:'../submitted-skills/Vinicius%20Nascimento/skills/am-i-free/am_i_free.py', kind:'script' }], 'back-to-work': [{ name:'SKILL.md', path:'../submitted-skills/Vinicius%20Nascimento/skills/back-to-work/SKILL.md', kind:'skill' }, { name:'back.sh', path:'../submitted-skills/Vinicius%20Nascimento/skills/back-to-work/back.sh', kind:'script' }], 'long-day-start': [{ name:'SKILL.md', path:'../submitted-skills/Vinicius%20Nascimento/skills/long-day-start/SKILL.md', kind:'skill' }, { name:'start.sh', path:'../submitted-skills/Vinicius%20Nascimento/skills/long-day-start/start.sh', kind:'script' }], diff --git a/skills-review/index.html b/skills-review/index.html index ad06c1e..c0eb0e9 100644 --- a/skills-review/index.html +++ b/skills-review/index.html @@ -6,6 +6,7 @@ Submitted Skills — Review Desk +
diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/SKILL.md b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/SKILL.md similarity index 96% rename from submitted-skills/Pedro Aranha/skills/ndo-repro/SKILL.md rename to submitted-skills/Anonymous Operational Submission/skills/ndo-repro/SKILL.md index 01ee6dd..4f3fedd 100644 --- a/submitted-skills/Pedro Aranha/skills/ndo-repro/SKILL.md +++ b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/SKILL.md @@ -42,7 +42,7 @@ ndo-ship.sh build [--ticket 231239] [--skip-tests] [--no-cache] - Java services: runs `mvn -B -DskipTests package` after the tests so `target/*.jar` exists for the `COPY`. - Builds `--platform linux/amd64`. **Never drop this** — the Mac is arm64, the nodes are amd64, and the mismatch only surfaces as a crashlooping pod after deploy. - Uses `Dockerfile_local` if present, else `Dockerfile`, and `--target release` when the dockerfile has stages. See `reference/dockerfile-local.md` before writing one. -- Image ref: `artifactorycn.netcracker.com:17009//_unm_:`. Ticket is parsed from the git branch (`bugfix/UNM-231239` → `231239`). The timestamp tag matters: deployments run `imagePullPolicy: IfNotPresent`, so a reused tag silently keeps the old image. +- Image ref: `[REDACTED REGISTRY]/<[REDACTED USER]>/_unm_:`. Ticket is parsed from the git branch (`bugfix/UNM-231239` → `231239`). The timestamp tag matters: deployments run `imagePullPolicy: IfNotPresent`, so a reused tag silently keeps the old image. The ref is cached, so `push`/`deploy` need no `--tag`. diff --git a/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/envs.tsv b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/envs.tsv new file mode 100644 index 0000000..16c2d06 --- /dev/null +++ b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/envs.tsv @@ -0,0 +1,3 @@ +# Published review fixture — original environment identities and endpoints removed. +# alias context namespace gateway +sample/dev [REDACTED CONTEXT] [REDACTED NAMESPACE] [REDACTED URL] diff --git a/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/lib/env.sh b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/lib/env.sh new file mode 100644 index 0000000..7bfe9fa --- /dev/null +++ b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/lib/env.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Published review fixture — original environment discovery and endpoints removed. + +_ndo_die() { echo "$*" >&2; exit 2; } + +env_list() { + printf '%-16s %-34s %-14s %s\n' ALIAS CONTEXT NAMESPACE GATEWAY + printf '%-16s %-34s %-14s %s\n' sample/dev '[REDACTED CONTEXT]' '[REDACTED NAMESPACE]' '[REDACTED URL]' +} + +env_resolve() { + _ndo_die "Environment resolution is disabled in this published, redacted review fixture." +} + +env_discover() { + _ndo_die "Environment discovery is disabled in this published, redacted review fixture." +} diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/ndo-api.sh b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-api.sh similarity index 100% rename from submitted-skills/Pedro Aranha/skills/ndo-repro/ndo-api.sh rename to submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-api.sh diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/ndo-ship.sh b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-ship.sh similarity index 98% rename from submitted-skills/Pedro Aranha/skills/ndo-repro/ndo-ship.sh rename to submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-ship.sh index e9d0061..e98fc47 100644 --- a/submitted-skills/Pedro Aranha/skills/ndo-repro/ndo-ship.sh +++ b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-ship.sh @@ -6,7 +6,7 @@ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=lib/env.sh source "$HERE/lib/env.sh" -REG="${NDO_REGISTRY:-artifactorycn.netcracker.com:17009}" +REG="${NDO_REGISTRY:-[REDACTED REGISTRY]}" ART_USER="${NDO_ARTIFACTORY_USER:-$USER}" PLATFORM="${NDO_PLATFORM:-linux/amd64}" PROJECTS="${NDO_PROJECTS:-$HOME/projects}" @@ -48,7 +48,7 @@ Options: --timeout D rollout wait (default 10m) -y, --yes confirm a cluster-mutating command (deploy/ship/rollback/pullsecret) -Image ref: $REG//_unm_: +Image ref: $REG/<[REDACTED USER]>/_unm_: Env overrides: NDO_REGISTRY NDO_ARTIFACTORY_USER NDO_PLATFORM NDO_PROJECTS NDO_ENV USAGE } diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/reference/bom-Dockerfile_local.example b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/bom-Dockerfile_local.example similarity index 86% rename from submitted-skills/Pedro Aranha/skills/ndo-repro/reference/bom-Dockerfile_local.example rename to submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/bom-Dockerfile_local.example index 8384252..60ad2d1 100644 --- a/submitted-skills/Pedro Aranha/skills/ndo-repro/reference/bom-Dockerfile_local.example +++ b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/bom-Dockerfile_local.example @@ -3,7 +3,7 @@ # and the shared_resources COPY (CI-injected, absent locally). # Copy to ~/projects/business-operation-manager/Dockerfile_local to use. -FROM artifactorycn.netcracker.com:17014/product/go-builder:1.26.4 AS base +FROM [REDACTED REGISTRY]/product/go-builder:1.26.4 AS base ENV APP_ROOT=/tmp/project COPY . ${APP_ROOT} @@ -15,7 +15,7 @@ RUN chmod -R u+x ${APP_ROOT}/scripts && \ FROM base AS build RUN cd ${APP_ROOT} && ${APP_ROOT}/application_build.sh -FROM artifactorycn.netcracker.com:17152/netcracker/qubership-core-base:2.3.7 AS release +FROM [REDACTED REGISTRY]/netcracker/qubership-core-base:2.3.7 AS release COPY --chown=10001:10001 --from=build /tmp/project/scripts/* /bin/ COPY --chown=10001:10001 --from=build /tmp/project/business-operation-manager /bin/app diff --git a/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/dockerfile-local.md b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/dockerfile-local.md new file mode 100644 index 0000000..2182926 --- /dev/null +++ b/submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/dockerfile-local.md @@ -0,0 +1,9 @@ +# Local Dockerfile note — redacted review fixture + +The original operational reference included internal source locations, registries, +and environment details. Those details have been removed from the published review. + +For a local Dockerfile guide, keep the general rule: use a project-owned local +override only when the ordinary Dockerfile requires CI-only inputs. Keep runtime +stages, explicit architecture handling, and the application artifact; never copy +credentials, internal endpoints, or personal registry paths into the override. diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/envs.tsv b/submitted-skills/Pedro Aranha/skills/ndo-repro/envs.tsv deleted file mode 100644 index 9b0abd6..0000000 --- a/submitted-skills/Pedro Aranha/skills/ndo-repro/envs.tsv +++ /dev/null @@ -1,15 +0,0 @@ -oss-01/dev-1 pedro.aranha-saas-rnd-oss-01 dev-1-oss https://public-gateway-dev-1-oss.saas-rnd-oss-01.managed.netcracker.cloud -oss-01/dev-2 pedro.aranha-saas-rnd-oss-01 dev-2-oss https://public-gateway-dev-2-oss.saas-rnd-oss-01.managed.netcracker.cloud -oss-01/dev-3 pedro.aranha-saas-rnd-oss-01 dev-3-oss https://public-gateway-dev-3-oss.saas-rnd-oss-01.managed.netcracker.cloud -oss-01/dev-4 pedro.aranha-saas-rnd-oss-01 dev-4-oss https://public-gateway-dev-4-oss.saas-rnd-oss-01.managed.netcracker.cloud -oss-02/dev-0 pedro.aranha-saas-rnd-oss-02 dev-0-oss https://public-gateway-dev-0-oss.saas-rnd-oss-02.managed.netcracker.cloud -oss-02/dev-2 pedro.aranha-saas-rnd-oss-02 dev-2-oss https://public-gateway-dev-2-oss.saas-rnd-oss-02.managed.netcracker.cloud -oss-02/dev-3 pedro.aranha-saas-rnd-oss-02 dev-3-oss https://public-gateway-dev-3-oss.saas-rnd-oss-02.managed.netcracker.cloud -oss-02/dev-4 pedro.aranha-saas-rnd-oss-02 dev-4-oss https://public-gateway-dev-4-oss.saas-rnd-oss-02.managed.netcracker.cloud -oss-03/dev-0 pedro.aranha-saas-rnd-oss-03 dev-0-oss https://public-gateway-dev-0-oss.saas-rnd-oss-03.managed.netcracker.cloud -oss-03/dev-1 pedro.aranha-saas-rnd-oss-03 dev-1-oss https://public-gateway-dev-1-oss.saas-rnd-oss-03.managed.netcracker.cloud -oss-03/dev-2 pedro.aranha-saas-rnd-oss-03 dev-2-oss https://public-gateway-dev-2-oss.saas-rnd-oss-03.managed.netcracker.cloud -oss-03/dev-3 pedro.aranha-saas-rnd-oss-03 dev-3-oss https://public-gateway-dev-3-oss.saas-rnd-oss-03.managed.netcracker.cloud -shared-244 ndo-shared-244 ndo https://public-gateway-ndo.ndo-shared-244.managed.netcracker.cloud -shared-244/ndo-at ndo-shared-244 ndo-at https://public-gateway-ndo-at.ndo-shared-244.managed.netcracker.cloud -shared-244/ndo-dev ndo-shared-244 ndo-dev https://public-gateway-ndo-dev.ndo-shared-244.managed.netcracker.cloud diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/lib/env.sh b/submitted-skills/Pedro Aranha/skills/ndo-repro/lib/env.sh deleted file mode 100644 index 70d2b90..0000000 --- a/submitted-skills/Pedro Aranha/skills/ndo-repro/lib/env.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash -# Shared env resolution for the ndo-repro skill. Source this; do not execute. -# Exports NDO_CTX (kube context), NDO_NS (namespace), NDO_GW (gateway base URL). - -NDO_CACHE="${NDO_CACHE:-$HOME/.cache/ndo-repro}" -NDO_ENV_FILE="${NDO_ENV_FILE:-$NDO_CACHE/envs.tsv}" -NDO_ENV_SEED="${NDO_ENV_SEED:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/envs.tsv}" -NDO_MARKER="${NDO_MARKER:-consolidated-inventory-manager-v1}" -NDO_NS_RE="${NDO_NS_RE:-^(ndo|ndo-dev|ndo-at|dev-[0-9]+-oss)$}" - -_ndo_die() { echo "$*" >&2; exit 2; } - -env_file() { - [ -s "$NDO_ENV_FILE" ] && { echo "$NDO_ENV_FILE"; return; } - mkdir -p "$NDO_CACHE" - [ -s "$NDO_ENV_SEED" ] && cp "$NDO_ENV_SEED" "$NDO_ENV_FILE" - echo "$NDO_ENV_FILE" -} - -env_list() { - printf '%-16s %-34s %-14s %s\n' ALIAS CONTEXT NAMESPACE GATEWAY - awk -F'\t' '!/^#/ && NF>=4 {printf "%-16s %-34s %-14s %s\n",$1,$2,$3,$4}' "$(env_file)" -} - -# env_resolve -> sets NDO_CTX / NDO_NS / NDO_GW -env_resolve() { - local want="${1:-}" f hits n - [ -n "$want" ] || _ndo_die "no env given. Use -e or NDO_ENV=. Known: -$(env_list)" - f="$(env_file)" - hits=$(awk -F'\t' -v w="$want" '!/^#/ && NF>=4 && ($1==w || $1 ~ "/" w "$")' "$f") - n=$(printf '%s' "$hits" | grep -c . || true) - [ "$n" -eq 0 ] && _ndo_die "unknown env '$want'. Known: -$(env_list) -Run: ndo-api.sh env discover" - [ "$n" -gt 1 ] && _ndo_die "ambiguous env '$want' — matches: -$(printf '%s\n' "$hits" | cut -f1) -Use the full alias (e.g. oss-01/$want)." - NDO_CTX=$(printf '%s' "$hits" | cut -f2) - NDO_NS=$(printf '%s' "$hits" | cut -f3) - NDO_GW=$(printf '%s' "$hits" | cut -f4) - export NDO_CTX NDO_NS NDO_GW -} - -# Short cluster alias: pedro.aranha-saas-rnd-oss-01 -> oss-01 ; ndo-shared-244 -> shared-244 -_cluster_alias() { sed -E 's/^.*saas-rnd-//; s/^ndo-//' <<<"$1"; } -# Short env alias: dev-1-oss -> dev-1 ; ndo -> (cluster alias only) -_ns_alias() { sed -E 's/-oss$//' <<<"$1"; } - -env_discover() { - local out ctx nss ns host alias calias nalias - mkdir -p "$NDO_CACHE" - out="$NDO_CACHE/envs.tsv.new" - : > "$out" - for ctx in $(kubectl config get-contexts -o name 2>/dev/null); do - case "$ctx" in orbstack|docker-desktop|minikube|kind-*) continue ;; esac - nss=$(timeout 25 kubectl --context="$ctx" get ns -o name 2>/dev/null | sed 's|namespace/||' | grep -E "$NDO_NS_RE") || continue - calias=$(_cluster_alias "$ctx") - for ns in $nss; do - timeout 20 kubectl --context="$ctx" -n "$ns" get deploy "$NDO_MARKER" -o name >/dev/null 2>&1 || continue - host=$(timeout 20 kubectl --context="$ctx" -n "$ns" get ingress public-gateway \ - -o jsonpath='{.spec.rules[0].host}' 2>/dev/null) - [ -n "$host" ] || host="public-gateway-${ns}.$(sed -E 's/^.*(saas-rnd-[a-z0-9-]+|ndo-[a-z0-9-]+)$/\1/' <<<"$ctx").managed.netcracker.cloud" - nalias=$(_ns_alias "$ns") - if [ "$nalias" = "ndo" ]; then alias="$calias"; else alias="$calias/$nalias"; fi - printf '%s\t%s\t%s\thttps://%s\n' "$alias" "$ctx" "$ns" "$host" >> "$out" - echo "found $alias -> $ctx/$ns" >&2 - done - done - [ -s "$out" ] || _ndo_die "discovery found no envs (VPN down? kube creds expired?) — kept $NDO_ENV_FILE" - sort -o "$out" "$out" - mv "$out" "$NDO_ENV_FILE" - env_list -} diff --git a/submitted-skills/Pedro Aranha/skills/ndo-repro/reference/dockerfile-local.md b/submitted-skills/Pedro Aranha/skills/ndo-repro/reference/dockerfile-local.md deleted file mode 100644 index e8e43cc..0000000 --- a/submitted-skills/Pedro Aranha/skills/ndo-repro/reference/dockerfile-local.md +++ /dev/null @@ -1,50 +0,0 @@ -# Dockerfile_local - -`Dockerfile_local` is the CI `Dockerfile` with the parts that only work on a Jenkins agent removed, so it builds on a laptop. Upstream example (Go service): - - -Create one only when the plain `Dockerfile` fails locally. `ndo-ship.sh` picks `Dockerfile_local` automatically when present, otherwise falls back to `Dockerfile`. - -## What to strip from the CI Dockerfile -- `COPY`/`ADD` of shared resources, config bundles, or licence files injected by the pipeline. -- `ARG`s the pipeline fills (DB hosts, wiremock hosts, credentials) — hardcode a dev value or drop the stage. -- Integration/`test` stages that need Mongo/Postgres/Arango/Kafka. Keep pure unit tests only, or run tests outside Docker. -- `test-report` / coverage export stages — dead weight for a repro image. - -## What must stay -- A stage named `release` — `ndo-ship.sh` builds `--target release` when the dockerfile has stages. -- The runtime base image and every `COPY` that puts the binary/jar plus its runtime resources in place. - -## Java / Maven services (CIM, device-library, …) -Their `Dockerfile` is single-stage and copies a prebuilt jar: - -```dockerfile -COPY --chown=10001:10001 target/consolidated-inventory-manager*.jar /app/app.jar -``` - -`ndo-ship.sh` detects `pom.xml` + a `COPY … target/` line and runs `mvn -B -DskipTests package` before `docker build`, so the jar exists. No `Dockerfile_local` is needed unless the base image or an `apk` mirror is unreachable from the laptop. - -If the `apk add` step fails (internal `yumsrv03cn` mirror unreachable off-VPN), that layer only installs fonts — a `Dockerfile_local` that drops it is fine for a repro image: - -```dockerfile -FROM artifactorycn.netcracker.com:17003/alpine/openjdk17:17.0.18.8.03 AS release -USER root -COPY --chown=10001:10001 target/consolidated-inventory-manager*.jar /app/app.jar -USER 10001:10001 -CMD ["java", "-jar", "/app/app.jar"] -``` - -A working example that built and deployed cleanly is kept alongside this file: `bom-Dockerfile_local.example` (business-operation-manager, verified 2026-08-12). - -## Go services (BOM, monitoring-*, …) -Already multi-stage with `base` / `test` / `build` / `release`. The usual local-only edits: drop the `test` stage's external `ARG` hosts, and drop `COPY … /shared_resources` if the pipeline generates it. - -The Go build stages already pin `GOARCH=amd64`, so they cross-compile fine, but the **runtime** stage still needs `--platform linux/amd64` (see below). - -## Architecture — the trap -The Mac is arm64; the clusters are amd64. Without `--platform linux/amd64` the image builds and pushes fine, then the pod dies with `exec format error` or `no match for platform in manifest`. `ndo-ship.sh` passes `--platform linux/amd64` by default; do not remove it. - -An amd64 build on an arm64 host runs under emulation, so the maven/go steps inside Docker are slow. That is why `ndo-ship.sh` runs Maven natively on the host and only the image assembly under Docker. - -## Registry -`artifactorycn.netcracker.com:17009` is the personal/dev repo — images land under `/…`. Product images live in `:17099` and `:17003`; never push there.